diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 065b0512a6..8cb5c8bcac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -150,11 +150,12 @@ test_build_system: test_report: stage: test_report + image: espressif/esp32-ci-env only: - master - triggers tags: - - test_report + - report variables: LOG_PATH: "$CI_PROJECT_DIR/$CI_BUILD_REF" TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test" @@ -166,12 +167,34 @@ test_report: - $LOG_PATH expire_in: 12 mos script: + # calc log path + - VER_NUM=`git rev-list HEAD | wc -l | awk '{print $1}'` + - SHA_ID=`echo $CI_BUILD_REF | cut -c 1-7` + - REVISION="${VER_NUM}_${SHA_ID}" + # replace / to _ in branch name + - ESCAPED_BRANCH_NAME=`echo $CI_BUILD_REF_NAME | sed 's/\//___/g'` + # result path and artifacts path + - RESULT_PATH="$CI_PROJECT_NAME/$ESCAPED_BRANCH_NAME/$REVISION" + - ARTIFACTS_PATH="$GITLAB_HTTP_SERVER/idf/esp-idf/builds/$CI_BUILD_ID/artifacts/browse/$CI_BUILD_REF" # clone test bench - git clone $GITLAB_SSH_SERVER/yinling/auto_test_script.git - cd auto_test_script # generate report - - python CITestReport.py -l $LOG_PATH -t $TEST_CASE_FILE_PATH -p $REPORT_PATH - + - TEST_RESULT=Pass + - python CITestReport.py -l $LOG_PATH -t $TEST_CASE_FILE_PATH -p $REPORT_PATH -r $RESULT_PATH -a $ARTIFACTS_PATH || TEST_RESULT=Fail + # commit to CI-test-result project + - git clone $GITLAB_SSH_SERVER/qa/CI-test-result.git + - rm -rf CI-test-result/RawData/$RESULT_PATH + - cp -R $CI_PROJECT_NAME CI-test-result/RawData + - cd CI-test-result + # config git user + - git config --global user.email "ci-test-result@espressif.com" + - git config --global user.name "ci-test-result" + # commit test result + - git add . + - git commit . -m "update test result for $CI_PROJECT_NAME/$CI_BUILD_REF_NAME/$CI_BUILD_REF, pipeline ID $CI_PIPELINE_ID" || exit 0 + - git push origin master + - test "${TEST_RESULT}" = "Pass" || exit 1 push_master_to_github: before_script: diff --git a/.gitmodules b/.gitmodules index c26f92e80c..9cba0ec5ef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "components/micro-ecc/micro-ecc"] path = components/micro-ecc/micro-ecc url = https://github.com/kmackay/micro-ecc.git +[submodule "components/coap/libcoap"] + path = components/coap/libcoap + url = https://github.com/obgm/libcoap.git diff --git a/README.md b/README.md index f3007fdb88..64fe3ed365 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,36 @@ -# Using Espressif IoT Development Framework with the ESP32 +# Espressif IoT Development Framework [![alt text](https://readthedocs.org/projects/docs/badge/?version=latest "Documentation Status")](http://esp-idf.readthedocs.io/en/latest/?badge=latest) -# Setting Up ESP-IDF +ESP-IDF is the official development framework for the `ESP32 `_ chip. -In the [docs](docs) directory you will find per-platform setup guides: +# Developing With the ESP-IDF -* [Windows Setup Guide](docs/windows-setup.rst) -* [Mac OS Setup Guide](docs/macos-setup.rst) -* [Linux Setup Guide](docs/linux-setup.rst) +## Setting Up ESP-IDF -# Finding A Project +See setup guides for detailed instructions to set up the ESP-IDF: -As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in the setup guide, esp-idf comes with some example projects in the [examples](examples) directory. +* [Windows Setup Guide](http://esp-idf.readthedocs.io/en/latest/windows-setup.html) +* [Mac OS Setup Guide](http://esp-idf.readthedocs.io/en/latest/macos-setup.html) +* [Linux Setup Guide](http://esp-idf.readthedocs.io/en/latest/linux-setup.html) -Once you've found the project you want to work with, change to its directory and you can configure and build it: +## Finding a Project -# Configuring your project +As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in the setup guide, 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. + +## Configuring the Project `make menuconfig` -# Compiling your project +## Compiling the Project `make all` ... will compile app, bootloader and generate a partition table based on the config. -# Flashing your project +## Flashing the Project When `make all` finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this from make by running: @@ -36,7 +40,17 @@ This will flash the entire project (app, bootloader and partition table) to a ne You don't need to run `make all` before running `make flash`, `make flash` will automatically rebuild anything which needs it. -# Compiling & Flashing Just the App +## Viewing Serial Output + +The `make monitor` target will use the already-installed [miniterm](http://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.miniterm) (a part of pyserial) to display serial output from the ESP32 on the terminal console. + +Exit miniterm by typing Ctrl-]. + +To flash and monitor output in one pass, you can run: + +`make flash monitor` + +## Compiling & Flashing Just the App After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table: @@ -47,7 +61,17 @@ After the initial flash, you may just want to build and flash just your app, not (There's no downside to reflashing the bootloader and partition table each time, if they haven't changed.) -# The Partition Table +## Parallel Builds + +ESP-IDF supports compiling multiple files in parallel, so all of the above commands can be run as `make -jN` where `N` is the number of parallel make processes to run (generally N should be equal to or one more than the number of CPU cores in your system.) + +Multiple make functions can be combined into one. For example: to build the app & bootloader using 5 jobs in parallel, then flash everything, and then display serial output from the ESP32 run: + +``` +make -j5 flash monitor +``` + +## The Partition Table Once you've compiled your project, the "build" directory will contain a binary file with a name like "my_app.bin". This is an ESP32 image binary that can be loaded by the bootloader. @@ -64,7 +88,7 @@ In both cases the factory app is flashed at offset 0x10000. If you `make partiti For more details about partition tables and how to create custom variations, view the `docs/partition-tables.rst` file. -# Erasing Flash +## Erasing Flash The `make 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 `make erase_flash`. @@ -72,11 +96,11 @@ This can be combined with other targets, ie `make erase_flash flash` will erase # Resources -* The [docs directory of the esp-idf repository](docs) contains source of [esp-idf](http://esp-idf.readthedocs.io/) documentation. +* Documentation for the latest version: http://esp-idf.readthedocs.io/. This documentation is built from the [docs directory](docs) of this repository. * The [esp32.com forum](http://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](http://esp-idf.readthedocs.io/en/latest/contributing.html>). +* If you're interested in contributing to ESP-IDF, please check the [Contributions Guide](http://esp-idf.readthedocs.io/en/latest/contributing.html>). diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index e95937256f..439117b596 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -378,4 +378,4 @@ const esp_partition_t *esp_ota_get_boot_partition(void) ESP_LOGE(TAG, "not found current bin"); return NULL; } -} \ No newline at end of file +} diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 35be94e4a3..9e7b17b65f 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -50,11 +50,14 @@ else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH # One time flashing requires user to run esptool.py command themselves, # and warning is printed about inability to reflash. +# +# The flashing command is deliberately printed without an auto-reset +# step, so the device doesn't immediately reset to flash itself. bootloader: $(BOOTLOADER_BIN) @echo $(SEPARATOR) @echo "Bootloader built. One-time flash command is:" - @echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)" + @echo "$(subst hard_reset,no_reset,$(ESPTOOLPY_WRITE_FLASH)) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)" @echo $(SEPARATOR) @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index d097083d2a..6b37899ac3 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -23,7 +23,6 @@ extern "C" #include "esp_flash_data_types.h" -#define BOOT_VERSION "V0.1" #define SPI_SEC_SIZE 0x1000 #define IROM_LOW 0x400D0000 #define IROM_HIGH 0x40400000 diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 7dd753b3f8..a374c4306d 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -25,6 +25,7 @@ #include "rom/rtc.h" #include "rom/uart.h" #include "rom/gpio.h" +#include "rom/secure_boot.h" #include "soc/soc.h" #include "soc/cpu.h" @@ -40,9 +41,11 @@ #include "esp_image_format.h" #include "esp_secure_boot.h" #include "esp_flash_encrypt.h" +#include "esp_flash_partitions.h" #include "bootloader_flash.h" - +#include "bootloader_random.h" #include "bootloader_config.h" +#include "rtc.h" extern int _bss_start; extern int _bss_end; @@ -116,16 +119,14 @@ bool load_partition_table(bootloader_state_t* bs) { const esp_partition_info_t *partitions; const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ - const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); char *partition_usage; - - ESP_LOGI(TAG, "Partition Table:"); - ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + esp_err_t err; + int num_partitions; #ifdef CONFIG_SECURE_BOOT_ENABLED if(esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying partition table signature..."); - esp_err_t err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify partition table signature."); return false; @@ -141,17 +142,21 @@ bool load_partition_table(bootloader_state_t* bs) } ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions); - for(int i = 0; i < MAX_PARTITIONS; i++) { + err = esp_partition_table_basic_verify(partitions, true, &num_partitions); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify partition table"); + return false; + } + + ESP_LOGI(TAG, "Partition Table:"); + ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + + for(int i = 0; i < num_partitions; i++) { const esp_partition_info_t *partition = &partitions[i]; ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition); ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype); partition_usage = "unknown"; - if (partition->magic != ESP_PARTITION_MAGIC) { - /* invalid partition definition indicates end-of-table */ - break; - } - /* valid partition table */ switch(partition->type) { case PART_TYPE_APP: /* app partition */ @@ -229,8 +234,15 @@ static bool ota_select_valid(const esp_ota_select_entry_t *s) void bootloader_main() { + /* Set CPU to 80MHz. + Start by ensuring it is set to XTAL, as PLL must be off first + (may still be on due to soft reset.) + */ + rtc_set_cpu_freq(CPU_XTAL); + rtc_set_cpu_freq(CPU_80M); + uart_console_configure(); - ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); + ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER); #if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_FLASH_ENCRYPTION_ENABLED) esp_err_t err; #endif @@ -248,6 +260,9 @@ void bootloader_main() REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); SPIUnlock(); + ESP_LOGI(TAG, "Enabling RNG early entropy source..."); + bootloader_random_enable(); + if(esp_image_load_header(0x1000, true, &fhdr) != ESP_OK) { ESP_LOGE(TAG, "failed to load bootloader header!"); return; @@ -359,6 +374,9 @@ void bootloader_main() } #endif + ESP_LOGI(TAG, "Disabling RNG early entropy source..."); + bootloader_random_disable(); + // copy loaded segments to RAM, set up caches for mapped segments, and start application ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); unpack_load_app(&load_part_pos); @@ -663,49 +681,28 @@ void print_flash_info(const esp_image_header_t* phdr) #endif } -#if CONFIG_CONSOLE_UART_CUSTOM -static uint32_t get_apb_freq(void) -{ - // Get the value of APB clock from RTC memory. - // The value is initialized in ROM code, and updated by librtc.a - // when APB clock is changed. - // This value is stored in RTC_CNTL_STORE5_REG as follows: - // RTC_CNTL_STORE5_REG = (freq >> 12) | ((freq >> 12) << 16) - uint32_t apb_freq_reg = REG_READ(RTC_CNTL_STORE5_REG); - uint32_t apb_freq_l = apb_freq_reg & 0xffff; - uint32_t apb_freq_h = apb_freq_reg >> 16; - if (apb_freq_l == apb_freq_h && apb_freq_l != 0) { - return apb_freq_l << 12; - } else { - // fallback value - return APB_CLK_FREQ_ROM; - } -} -#endif - static void uart_console_configure(void) { #if CONFIG_CONSOLE_UART_NONE ets_install_putc1(NULL); ets_install_putc2(NULL); #else // CONFIG_CONSOLE_UART_NONE + const int uart_num = CONFIG_CONSOLE_UART_NUM; + uartAttach(); ets_install_uart_printf(); -#if CONFIG_CONSOLE_UART_CUSTOM - // Some constants to make the following code less upper-case - const int uart_num = CONFIG_CONSOLE_UART_NUM; - const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; - const int uart_tx_gpio = CONFIG_CONSOLE_UART_TX_GPIO; - const int uart_rx_gpio = CONFIG_CONSOLE_UART_RX_GPIO; // ROM bootloader may have put a lot of text into UART0 FIFO. // Wait for it to be printed. uart_tx_wait_idle(0); + +#if CONFIG_CONSOLE_UART_CUSTOM + // Some constants to make the following code less upper-case + const int uart_tx_gpio = CONFIG_CONSOLE_UART_TX_GPIO; + const int uart_rx_gpio = CONFIG_CONSOLE_UART_RX_GPIO; // Switch to the new UART (this just changes UART number used for // ets_printf in ROM code). uart_tx_switch(uart_num); - // Set new baud rate - uart_div_modify(uart_num, (((uint64_t) get_apb_freq()) << 4) / uart_baud); // If console is attached to UART1 or if non-default pins are used, // need to reconfigure pins using GPIO matrix if (uart_num != 0 || uart_tx_gpio != 1 || uart_rx_gpio != 3) { @@ -722,5 +719,19 @@ static void uart_console_configure(void) gpio_matrix_in(uart_rx_gpio, rx_idx, 0); } #endif // CONFIG_CONSOLE_UART_CUSTOM + + // Set configured UART console baud rate + const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; + uart_div_modify(uart_num, (APB_CLK_FREQ << 4) / uart_baud); + #endif // CONFIG_CONSOLE_UART_NONE } + +/* empty rtc_printf implementation, to work with librtc + linking. Can be removed once -lrtc is removed from bootloader's + main component.mk. +*/ +int rtc_printf(void) +{ + return 0; +} diff --git a/components/bootloader/src/main/component.mk b/components/bootloader/src/main/component.mk index e98545fc47..73cd9287df 100644 --- a/components/bootloader/src/main/component.mk +++ b/components/bootloader/src/main/component.mk @@ -5,8 +5,19 @@ # we pull in bootloader-specific linker arguments. # -LINKER_SCRIPTS := esp32.bootloader.ld $(IDF_PATH)/components/esp32/ld/esp32.rom.ld +LINKER_SCRIPTS := \ + esp32.bootloader.ld \ + $(IDF_PATH)/components/esp32/ld/esp32.rom.ld \ + esp32.bootloader.rom.ld COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain $(addprefix -T ,$(LINKER_SCRIPTS)) COMPONENT_ADD_LINKER_DEPS := $(LINKER_SCRIPTS) + +ifdef IS_BOOTLOADER_BUILD +# following lines are a workaround to link librtc into the +# bootloader, until clock setting code is in a source-based esp-idf +# component. See also rtc_printf() in bootloader_start.c +COMPONENT_ADD_LDFLAGS += -L $(IDF_PATH)/components/esp32/lib/ -lrtc +COMPONENT_EXTRA_INCLUDES += $(IDF_PATH)/components/esp32/ +endif diff --git a/components/bootloader/src/main/esp32.bootloader.ld b/components/bootloader/src/main/esp32.bootloader.ld index 6a77eb6ade..500478814c 100644 --- a/components/bootloader/src/main/esp32.bootloader.ld +++ b/components/bootloader/src/main/esp32.bootloader.ld @@ -17,7 +17,7 @@ MEMORY dport0_seg (RW) : org = 0x3FF00000, len = 0x10 /* IO */ iram_seg (RWX) : org = 0x40080000, len = 0x400 /* 1k of IRAM used by bootloader functions which need to flush/enable APP CPU cache */ iram_pool_1_seg (RWX) : org = 0x40078000, len = 0x8000 /* IRAM POOL1, used for APP CPU cache. We can abuse it in bootloader because APP CPU is still held in reset, until we enable APP CPU cache */ - dram_seg (RW) : org = 0x3FFC0000, len = 0x20000 /* Shared RAM, minus rom bss/data/stack.*/ + dram_seg (RW) : org = 0x3FFF0000, len = 0x10000 /* 64k at the end of DRAM, after ROM bootloader stack */ } /* Default entry point: */ diff --git a/components/bootloader/src/main/esp32.bootloader.rom.ld b/components/bootloader/src/main/esp32.bootloader.rom.ld new file mode 100644 index 0000000000..70f83bdf54 --- /dev/null +++ b/components/bootloader/src/main/esp32.bootloader.rom.ld @@ -0,0 +1 @@ +PROVIDE ( ets_update_cpu_frequency = 0x40008550 ); /* Updates g_ticks_per_us on the current CPU only; not on the other core */ diff --git a/components/bootloader_support/include_priv/bootloader_random.h b/components/bootloader_support/include_priv/bootloader_random.h index 86d42e31d9..bb3b2a81dd 100644 --- a/components/bootloader_support/include_priv/bootloader_random.h +++ b/components/bootloader_support/include_priv/bootloader_random.h @@ -16,6 +16,30 @@ #include +/** + * @brief Enable early entropy source for RNG + * + * Uses the SAR ADC to feed entropy into the HWRNG. The ADC is put + * into a test mode that reads the 1.1V internal reference source and + * feeds the LSB of data into the HWRNG. + * + * Can also be used from app code early during operation, if entropy + * is required before WiFi stack is initialised. Call this function + * from app code only if WiFi/BT are not yet enabled and I2S and SAR + * ADC are not in use. + * + * Call bootloader_random_disable() when done. + */ +void bootloader_random_enable(void); + +/** + * @brief Disable early entropy source for RNG + * + * Disables SAR ADC source and resets the I2S hardware. + * + */ +void bootloader_random_disable(void); + /** * @brief Fill buffer with 'length' random bytes * diff --git a/components/bootloader_support/src/bootloader_random.c b/components/bootloader_support/src/bootloader_random.c index fd780c4b7b..b58ebe941d 100644 --- a/components/bootloader_support/src/bootloader_random.c +++ b/components/bootloader_support/src/bootloader_random.c @@ -12,37 +12,45 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "bootloader_random.h" +#include "soc/cpu.h" #include "soc/wdev_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" +#include "soc/syscon_reg.h" +#include "soc/dport_reg.h" +#include "soc/i2s_reg.h" +#include "esp_log.h" #ifndef BOOTLOADER_BUILD #include "esp_system.h" #endif +const char *TAG = "boot_rng"; + void bootloader_fill_random(void *buffer, size_t length) { uint8_t *buffer_bytes = (uint8_t *)buffer; uint32_t random; - - /* TODO: enable HW RNG clock - - Until this clock is enabled, this is not secure - */ +#ifdef BOOTLOADER_BUILD + uint32_t start, now; +#endif for (int i = 0; i < length; i++) { if (i == 0 || i % 4 == 0) { /* redundant check is for a compiler warning */ #ifdef BOOTLOADER_BUILD - /* HW RNG generates 32 bits entropy per 16 APB cycles, - in bootloader CPU clock == APB clock. + /* in bootloader with ADC feeding HWRNG, we accumulate 1 + bit of entropy per 40 APB cycles (==80 CPU cycles.) - We are being conservative here and waiting at least - that long, as loop shift overhead, etc will add more - cycles. + To avoid reading the entire RNG hardware state out + as-is, we repeatedly read the RNG register and XOR all + values. */ - asm volatile("nop; nop; nop; nop;"); - asm volatile("nop; nop; nop; nop;"); - asm volatile("nop; nop; nop; nop;"); - asm volatile("nop; nop; nop; nop;"); random = REG_READ(WDEV_RND_REG); + RSR(CCOUNT, start); + do { + random ^= REG_READ(WDEV_RND_REG); + RSR(CCOUNT, now); + } while(now - start < 80*32*2); /* extra factor of 2 is precautionary */ #else random = esp_random(); #endif @@ -51,3 +59,80 @@ void bootloader_fill_random(void *buffer, size_t length) buffer_bytes[i] = random >> ((i % 4) * 8); } } + +void bootloader_random_enable(void) +{ + /* Enable SAR ADC in test mode to feed ADC readings of the 1.1V + reference via I2S into the RNG entropy input. + + Note: I2S requires the PLL to be running, so the call to rtc_set_cpu_freq(CPU_80M) + in early bootloader startup must have been made. + */ + SET_PERI_REG_BITS(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_DTEST_RTC, 2, RTC_CNTL_DTEST_RTC_S); + SET_PERI_REG_MASK(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_ENT_RTC); + SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_SAR2_EN_TEST); + + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP); + CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_START_TOP); + // Test pattern configuration byte 0xAD: + //--[7:4] channel_sel: 10-->en_test + //--[3:2] bit_width : 3-->12bit + //--[1:0] atten : 1-->3dB attenuation + WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB1_REG, 0xADADADAD); + WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB2_REG, 0xADADADAD); + WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB3_REG, 0xADADADAD); + WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB4_REG, 0xADADADAD); + + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); + SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DIG_FORCE); + SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DIG_FORCE); + SET_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR2_MUX); + SET_PERI_REG_BITS(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR_CLK_DIV, 4, SYSCON_SARADC_SAR_CLK_DIV_S); + + SET_PERI_REG_BITS(SYSCON_SARADC_FSM_REG, SYSCON_SARADC_RSTB_WAIT, 8, SYSCON_SARADC_RSTB_WAIT_S); /* was 1 */ + SET_PERI_REG_BITS(SYSCON_SARADC_FSM_REG, SYSCON_SARADC_START_WAIT, 10, SYSCON_SARADC_START_WAIT_S); + SET_PERI_REG_BITS(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_WORK_MODE, 0, SYSCON_SARADC_WORK_MODE_S); + SET_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR_SEL); + CLEAR_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_DATA_SAR_SEL); + + SET_PERI_REG_BITS(I2S_SAMPLE_RATE_CONF_REG(0), I2S_RX_BCK_DIV_NUM, 20, I2S_RX_BCK_DIV_NUM_S); + + SET_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG,SYSCON_SARADC_DATA_TO_I2S); + + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_CAMERA_EN); + SET_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_LCD_EN); + SET_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE); + SET_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE_TEST_EN); + SET_PERI_REG_MASK(I2S_CONF_REG(0), I2S_RX_START); +} + +void bootloader_random_disable(void) +{ + /* Disable i2s clock */ + CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + + + /* Reset some i2s configuration (possibly redundant as we reset entire + I2S peripheral further down). */ + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_CAMERA_EN); + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_LCD_EN); + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE_TEST_EN); + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE); + CLEAR_PERI_REG_MASK(I2S_CONF_REG(0), I2S_RX_START); + + /* Restore SYSCON mode registers */ + CLEAR_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DIG_FORCE); + CLEAR_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DIG_FORCE); + + /* Restore SAR ADC mode */ + CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_SAR2_EN_TEST); + CLEAR_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR2_MUX + | SYSCON_SARADC_SAR_SEL | SYSCON_SARADC_DATA_TO_I2S); + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 0, SENS_FORCE_XPD_SAR_S); + SET_PERI_REG_BITS(SYSCON_SARADC_FSM_REG, SYSCON_SARADC_START_WAIT, 8, SYSCON_SARADC_START_WAIT_S); + + /* Reset i2s peripheral */ + SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST); +} diff --git a/components/bootloader_support/src/flash_encrypt.c b/components/bootloader_support/src/flash_encrypt.c index f4fd7e783d..8b9ca60cd1 100644 --- a/components/bootloader_support/src/flash_encrypt.c +++ b/components/bootloader_support/src/flash_encrypt.c @@ -78,12 +78,19 @@ static esp_err_t initialise_flash_encryption(void) && REG_READ(EFUSE_BLK1_RDATA5_REG) == 0 && REG_READ(EFUSE_BLK1_RDATA6_REG) == 0 && REG_READ(EFUSE_BLK1_RDATA7_REG) == 0) { + ESP_LOGI(TAG, "Generating new flash encryption key..."); + uint32_t buf[8]; + bootloader_fill_random(buf, sizeof(buf)); + for (int i = 0; i < 8; i++) { + ESP_LOGV(TAG, "EFUSE_BLK1_WDATA%d_REG = 0x%08x", i, buf[i]); + REG_WRITE(EFUSE_BLK1_WDATA0_REG + 4*i, buf[i]); + } + bzero(buf, sizeof(buf)); + esp_efuse_burn_new_values(); - /* On-device key generation is temporarily disabled, until - * RNG operation during bootloader is qualified. - * See docs/security/flash-encryption.rst for details. */ - ESP_LOGE(TAG, "On-device key generation is not yet available."); - return ESP_ERR_NOT_SUPPORTED; + ESP_LOGI(TAG, "Read & write protecting new key..."); + REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK1 | EFUSE_RD_DIS_BLK1); + esp_efuse_burn_new_values(); } else { if(!(efuse_key_read_protected && efuse_key_write_protected)) { diff --git a/components/bootloader_support/src/flash_partitions.c b/components/bootloader_support/src/flash_partitions.c index ed427df1a6..b60968f969 100644 --- a/components/bootloader_support/src/flash_partitions.c +++ b/components/bootloader_support/src/flash_partitions.c @@ -13,32 +13,43 @@ // limitations under the License. #include "esp_flash_partitions.h" #include "esp_log.h" +#include "rom/spi_flash.h" static const char *TAG = "flash_parts"; esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions) { int num_parts; + uint32_t chip_size = g_rom_flashchip.chip_size; *num_partitions = 0; for(num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) { const esp_partition_info_t *part = &partition_table[num_parts]; - if(part->magic == 0xFFFF - && part->type == PART_TYPE_END - && part->subtype == PART_SUBTYPE_END) { + if (part->magic == 0xFFFF + && part->type == PART_TYPE_END + && part->subtype == PART_SUBTYPE_END) { /* TODO: check md5 */ ESP_LOGD(TAG, "partition table verified, %d entries", num_parts); *num_partitions = num_parts; return ESP_OK; } - if(part->magic != ESP_PARTITION_MAGIC) { + if (part->magic != ESP_PARTITION_MAGIC) { if (log_errors) { ESP_LOGE(TAG, "partition %d invalid magic number 0x%x", num_parts, part->magic); } return ESP_ERR_INVALID_STATE; } + + const esp_partition_pos_t *pos = &part->pos; + if (pos->offset > chip_size || pos->offset + pos->size > chip_size) { + if (log_errors) { + ESP_LOGE(TAG, "partition %d invalid - offset 0x%x size 0x%x exceeds flash chip size 0x%x", + num_parts, pos->offset, pos->size, chip_size); + } + return ESP_ERR_INVALID_SIZE; + } } if (log_errors) { diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index c5ae1f19ea..0230e85ad5 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -130,12 +130,21 @@ esp_err_t esp_secure_boot_permanently_enable(void) { && REG_READ(EFUSE_BLK2_RDATA5_REG) == 0 && REG_READ(EFUSE_BLK2_RDATA6_REG) == 0 && REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) { + ESP_LOGI(TAG, "Generating new secure boot key..."); + uint32_t buf[8]; + bootloader_fill_random(buf, sizeof(buf)); + for (int i = 0; i < 8; i++) { + ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]); + REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]); + } + bzero(buf, sizeof(buf)); + burn_efuses(); + ESP_LOGI(TAG, "Read & write protecting new key..."); + REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2); + burn_efuses(); + efuse_key_read_protected = true; + efuse_key_write_protected = true; - /* On-device key generation is temporarily disabled, until - * RNG operation during bootloader is qualified. - * See docs/security/secure-boot.rst for details. */ - ESP_LOGE(TAG, "On-device key generation is not yet available."); - return ESP_ERR_NOT_SUPPORTED; } else { ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2"); } diff --git a/components/bt/Kconfig b/components/bt/Kconfig index 9a8014fa66..d2227868ce 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -1,41 +1,24 @@ -menu "BT config" -visible if MEMMAP_BT - - -config BT_ENABLED - bool - depends on ESP32_ENABLE_STACK_BT +menuconfig BT_ENABLED + bool "Bluetooth" help - This compiles in the low-level BT stack. + Select this option to enable Bluetooth stack and show the submenu with Bluetooth configuration choices. config BTC_TASK_STACK_SIZE - int "BT event (callback to application) task stack size" + int "Bluetooth event (callback to application) task stack size" + depends on BT_ENABLED default 3072 help This select btc task stack size config BLUEDROID_MEM_DEBUG bool "Bluedroid memory debug" - default no + depends on BT_ENABLED + default n help Bluedroid memory debug -#config BT_BTLE -# bool "Enable BTLE" -# depends on BT_ENABLED -# help -# This compiles BTLE support -# -#config BT_BT -# bool "Enable classic BT" -# depends on BT_ENABLED -# help -# This enables classic BT support - -endmenu - # Memory reserved at start of DRAM for Bluetooth stack config BT_RESERVE_DRAM hex - default 0x10000 if MEMMAP_BT + default 0x10000 if BT_ENABLED default 0 diff --git a/components/bt/bluedroid/api/esp_blufi_api.c b/components/bt/bluedroid/api/esp_blufi_api.c index a74c9d04b5..2fcbbab794 100644 --- a/components/bt/bluedroid/api/esp_blufi_api.c +++ b/components/bt/bluedroid/api/esp_blufi_api.c @@ -22,22 +22,38 @@ #include "btc_main.h" #include "future.h" -esp_err_t esp_blufi_register_callback(esp_profile_cb_t callback) +esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks) { - return (btc_profile_cb_set(BTC_PID_BLUFI, callback) == 0 ? ESP_OK : ESP_FAIL); + if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { + return ESP_ERR_INVALID_STATE; + } + + if (callbacks == NULL) { + return ESP_FAIL; + } + + btc_blufi_set_callbacks(callbacks); + return (btc_profile_cb_set(BTC_PID_BLUFI, callbacks->event_cb) == 0 ? ESP_OK : ESP_FAIL); } -esp_err_t esp_blufi_send_config_state(esp_blufi_config_state_t state) +esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_t opmode, esp_blufi_sta_conn_state_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *extra_info) { btc_msg_t msg; btc_blufi_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; - msg.act = BTC_BLUFI_ACT_SEND_CFG_STATE; - arg.cfg_state.state = state; + msg.act = BTC_BLUFI_ACT_SEND_CFG_REPORT; + arg.wifi_conn_report.opmode = opmode; + arg.wifi_conn_report.sta_conn_state = sta_conn_state; + arg.wifi_conn_report.softap_conn_num = softap_conn_num; + arg.wifi_conn_report.extra_info = extra_info; - return (btc_transfer_context(&msg, &arg, sizeof(btc_blufi_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_blufi_args_t), btc_blufi_call_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } @@ -45,6 +61,10 @@ esp_err_t esp_blufi_profile_init(void) { btc_msg_t msg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_INIT; @@ -56,6 +76,10 @@ esp_err_t esp_blufi_profile_deinit(void) { btc_msg_t msg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_DEINIT; @@ -63,4 +87,8 @@ esp_err_t esp_blufi_profile_deinit(void) return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } +uint16_t esp_blufi_get_version(void) +{ + return btc_blufi_get_version(); +} diff --git a/components/bt/bluedroid/api/esp_bt_device.c b/components/bt/bluedroid/api/esp_bt_device.c new file mode 100644 index 0000000000..dffcbe80b2 --- /dev/null +++ b/components/bt/bluedroid/api/esp_bt_device.c @@ -0,0 +1,26 @@ +// Copyright 2015-2016 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. + + +#include "esp_bt_device.h" +#include "esp_bt_main.h" +#include "controller.h" + +const uint8_t *esp_bt_dev_get_address(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return NULL; + } + return controller_get_interface()->get_address()->address; +} diff --git a/components/bt/bluedroid/api/esp_bt_main.c b/components/bt/bluedroid/api/esp_bt_main.c index f185999287..c9fe4fc060 100644 --- a/components/bt/bluedroid/api/esp_bt_main.c +++ b/components/bt/bluedroid/api/esp_bt_main.c @@ -21,7 +21,20 @@ static bool esp_already_enable = false; static bool esp_already_init = false; -esp_err_t esp_enable_bluetooth(void) +esp_bluedroid_status_t esp_bluedroid_get_status(void) +{ + if (esp_already_init) { + if (esp_already_enable) { + return ESP_BLUEDROID_STATUS_ENABLED; + } else { + return ESP_BLUEDROID_STATUS_INITIALIZED; + } + } else { + return ESP_BLUEDROID_STATUS_UNINITIALIZED; + } +} + +esp_err_t esp_bluedroid_enable(void) { btc_msg_t msg; future_t **future_p; @@ -53,7 +66,7 @@ esp_err_t esp_enable_bluetooth(void) return ESP_OK; } -esp_err_t esp_disable_bluetooth(void) +esp_err_t esp_bluedroid_disable(void) { btc_msg_t msg; future_t **future_p; @@ -85,7 +98,7 @@ esp_err_t esp_disable_bluetooth(void) return ESP_OK; } -esp_err_t esp_init_bluetooth(void) +esp_err_t esp_bluedroid_init(void) { btc_msg_t msg; future_t **future_p; @@ -114,13 +127,13 @@ esp_err_t esp_init_bluetooth(void) return ESP_FAIL; } - esp_already_init = true;; + esp_already_init = true; return ESP_OK; } -esp_err_t esp_deinit_bluetooth(void) +esp_err_t esp_bluedroid_deinit(void) { btc_msg_t msg; future_t **future_p; diff --git a/components/bt/bluedroid/api/esp_gap_ble_api.c b/components/bt/bluedroid/api/esp_gap_ble_api.c index 0770fcfed5..56bedc4242 100644 --- a/components/bt/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/bluedroid/api/esp_gap_ble_api.c @@ -14,6 +14,7 @@ #include +#include "esp_bt_main.h" #include "esp_gap_ble_api.h" #include "bta_api.h" #include "bt_trace.h" @@ -21,8 +22,11 @@ #include "btc_gap_ble.h" -esp_err_t esp_ble_gap_register_callback(esp_profile_cb_t callback) +esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback) { + if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { + return ESP_ERR_INVALID_STATE; + } return (btc_profile_cb_set(BTC_PID_GAP_BLE, callback) == 0 ? ESP_OK : ESP_FAIL); } @@ -31,7 +35,11 @@ esp_err_t esp_ble_gap_config_adv_data(esp_ble_adv_data_t *adv_data) { btc_msg_t msg; btc_ble_gap_args_t arg; - + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (adv_data == NULL) { return ESP_ERR_INVALID_ARG; } @@ -54,7 +62,11 @@ esp_err_t esp_ble_gap_set_scan_params(esp_ble_scan_params_t *scan_params) { btc_msg_t msg; btc_ble_gap_args_t arg; - + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (scan_params == NULL) { return ESP_ERR_INVALID_ARG; } @@ -72,6 +84,10 @@ esp_err_t esp_ble_gap_start_scanning(uint32_t duration) btc_msg_t msg; btc_ble_gap_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_START_SCAN; @@ -85,6 +101,10 @@ esp_err_t esp_ble_gap_stop_scanning(void) { btc_msg_t msg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_STOP_SCAN; @@ -96,6 +116,10 @@ esp_err_t esp_ble_gap_start_advertising(esp_ble_adv_params_t *adv_params) btc_msg_t msg; btc_ble_gap_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_START_ADV; @@ -108,6 +132,10 @@ esp_err_t esp_ble_gap_stop_advertising(void) { btc_msg_t msg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_STOP_ADV; @@ -121,6 +149,10 @@ esp_err_t esp_ble_gap_update_conn_params(esp_ble_conn_update_params_t *params) btc_msg_t msg; btc_ble_gap_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_UPDATE_CONN_PARAM; @@ -134,6 +166,10 @@ esp_err_t esp_ble_gap_set_pkt_data_len(esp_bd_addr_t remote_device, uint16_t tx_ btc_msg_t msg; btc_ble_gap_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_SET_PKT_DATA_LEN; @@ -149,6 +185,10 @@ esp_err_t esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) btc_msg_t msg; btc_ble_gap_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_SET_RAND_ADDRESS; @@ -163,6 +203,10 @@ esp_err_t esp_ble_gap_config_local_privacy (bool privacy_enable) btc_msg_t msg; btc_ble_gap_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_CONFIG_LOCAL_PRIVACY; @@ -176,6 +220,10 @@ esp_err_t esp_ble_gap_set_device_name(const char *name) btc_msg_t msg; btc_ble_gap_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (strlen(name) > ESP_GAP_DEVICE_NAME_MAX) { return ESP_ERR_INVALID_ARG; } diff --git a/components/bt/bluedroid/api/esp_gattc_api.c b/components/bt/bluedroid/api/esp_gattc_api.c index c856947f01..4f68d81cc0 100644 --- a/components/bt/bluedroid/api/esp_gattc_api.c +++ b/components/bt/bluedroid/api/esp_gattc_api.c @@ -15,11 +15,17 @@ #include #include "esp_gattc_api.h" +#include "esp_bt_main.h" #include "btc_manage.h" #include "btc_gattc.h" +#include "btc_gatt_util.h" -esp_err_t esp_ble_gattc_register_callback(esp_profile_cb_t callback) +esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback) { + if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { + return ESP_ERR_INVALID_STATE; + } + if (callback == NULL) { return ESP_FAIL; } @@ -33,7 +39,10 @@ esp_err_t esp_ble_gattc_app_register(uint16_t app_id) btc_msg_t msg; btc_ble_gattc_args_t arg; - //if (app_id < ESP_APP_ID_MIN || app_id > ESP_APP_ID_MAX) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (app_id > ESP_APP_ID_MAX) { return ESP_ERR_INVALID_ARG; } @@ -46,52 +55,68 @@ esp_err_t esp_ble_gattc_app_register(uint16_t app_id) return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gatt_if) +esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gattc_if) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_APP_UNREGISTER; - arg.app_unreg.gatt_if = gatt_if; + arg.app_unreg.gattc_if = gattc_if; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bool is_direct) +esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, bool is_direct) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_OPEN; - arg.open.gatt_if = gatt_if; + arg.open.gattc_if = gattc_if; memcpy(arg.open.remote_bda, remote_bda, ESP_BD_ADDR_LEN); arg.open.is_direct = is_direct; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_close (uint16_t conn_id) +esp_err_t esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_CLOSE; - arg.close.conn_id = conn_id; + arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_config_mtu (uint16_t conn_id, uint16_t mtu) +esp_err_t esp_ble_gattc_config_mtu (esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t mtu) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if ((mtu < ESP_GATT_DEF_BLE_MTU_SIZE) || (mtu > ESP_GATT_MAX_MTU_SIZE)) { return ESP_GATT_ILLEGAL_PARAMETER; } @@ -99,21 +124,26 @@ esp_err_t esp_ble_gattc_config_mtu (uint16_t conn_id, uint16_t mtu) msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_CFG_MTU; - arg.cfg_mtu.conn_id = conn_id; + arg.cfg_mtu.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); arg.cfg_mtu.mtu = mtu; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_search_service(uint16_t conn_id, esp_bt_uuid_t *filter_uuid) +esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *filter_uuid) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_SEARCH_SERVICE; - arg.search_srvc.conn_id = conn_id; + arg.search_srvc.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + if (filter_uuid) { arg.search_srvc.filter_uuid_enable = true; memcpy(&arg.search_srvc.filter_uuid, filter_uuid, sizeof(esp_bt_uuid_t)); @@ -124,22 +154,27 @@ esp_err_t esp_ble_gattc_search_service(uint16_t conn_id, esp_bt_uuid_t *filter_u return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_get_characteristic(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *start_char_id) +esp_err_t esp_ble_gattc_get_characteristic(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *start_char_id) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; if (start_char_id) { - arg.get_next_char.conn_id = conn_id; + arg.get_next_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_next_char.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.get_next_char.char_id, start_char_id, sizeof(esp_gatt_id_t)); msg.act = BTC_GATTC_ACT_GET_NEXT_CHAR; } else { - arg.get_first_char.conn_id = conn_id; + arg.get_first_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_first_char.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); msg.act = BTC_GATTC_ACT_GET_FIRST_CHAR; } @@ -147,7 +182,8 @@ esp_err_t esp_ble_gattc_get_characteristic(uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, +esp_err_t esp_ble_gattc_get_descriptor(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, esp_gatt_id_t *start_descr_id) @@ -155,17 +191,21 @@ esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; if (start_descr_id) { - arg.get_next_descr.conn_id = conn_id; + arg.get_next_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_next_descr.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.get_next_descr.char_id, char_id, sizeof(esp_gatt_id_t)); memcpy(&arg.get_next_descr.descr_id, start_descr_id, sizeof(esp_gatt_id_t)); msg.act = BTC_GATTC_ACT_GET_NEXT_DESCR; } else { - arg.get_first_descr.conn_id = conn_id; + arg.get_first_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_first_descr.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.get_first_descr.char_id, char_id, sizeof(esp_gatt_id_t)); msg.act = BTC_GATTC_ACT_GET_FIRST_DESCR; @@ -174,23 +214,28 @@ esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_get_included_service(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_srvc_id_t *start_incl_srvc_id) +esp_err_t esp_ble_gattc_get_included_service(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_srvc_id_t *start_incl_srvc_id) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; if (start_incl_srvc_id) { - arg.get_next_incl_srvc.conn_id = conn_id; + arg.get_next_incl_srvc.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_next_incl_srvc.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.get_next_incl_srvc.start_service_id, start_incl_srvc_id, sizeof(esp_gatt_srvc_id_t)); msg.act = BTC_GATTC_ACT_GET_NEXT_INCL_SERVICE; } else { - arg.get_first_incl_srvc.conn_id = conn_id; + arg.get_first_incl_srvc.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_first_incl_srvc.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); msg.act = BTC_GATTC_ACT_GET_FIRST_INCL_SERVICE; } @@ -198,16 +243,23 @@ esp_err_t esp_ble_gattc_get_included_service(uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_read_char (uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, esp_gatt_auth_req_t auth_req) +esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_auth_req_t auth_req) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_CHAR; - arg.read_char.conn_id = conn_id; + arg.read_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.read_char.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.read_char.char_id, char_id, sizeof(esp_gatt_id_t)); arg.read_char.auth_req = auth_req; @@ -215,19 +267,24 @@ esp_err_t esp_ble_gattc_read_char (uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_read_char_descr (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_id_t *descr_id, - esp_gatt_auth_req_t auth_req) +esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_id_t *descr_id, + esp_gatt_auth_req_t auth_req) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_CHAR_DESCR; - arg.read_descr.conn_id = conn_id; + arg.read_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.read_descr.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.read_descr.char_id, char_id, sizeof(esp_gatt_id_t)); memcpy(&arg.read_descr.descr_id, descr_id, sizeof(esp_gatt_id_t)); @@ -236,21 +293,26 @@ esp_err_t esp_ble_gattc_read_char_descr (uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, +esp_err_t esp_ble_gattc_write_char( esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, uint16_t value_len, uint8_t *value, - esp_gatt_write_type_t write_type, + esp_gatt_write_type_t write_type, esp_gatt_auth_req_t auth_req) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_WRITE_CHAR; - arg.write_char.conn_id = (uint16_t) conn_id; + arg.write_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.write_char.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.write_char.char_id, char_id, sizeof(esp_gatt_id_t)); arg.write_char.value_len = value_len > ESP_GATT_MAX_ATTR_LEN ? ESP_GATT_MAX_ATTR_LEN : value_len; @@ -261,22 +323,27 @@ esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_write_char_descr (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_id_t *descr_id, - uint16_t value_len, - uint8_t *value, - esp_gatt_write_type_t write_type, - esp_gatt_auth_req_t auth_req) +esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_id_t *descr_id, + uint16_t value_len, + uint8_t *value, + esp_gatt_write_type_t write_type, + esp_gatt_auth_req_t auth_req) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_WRITE_CHAR_DESCR; - arg.write_descr.conn_id = (uint16_t) conn_id; + arg.write_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.write_descr.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.write_descr.char_id, char_id, sizeof(esp_gatt_id_t)); memcpy(&arg.write_descr.descr_id, descr_id, sizeof(esp_gatt_id_t)); @@ -288,7 +355,8 @@ esp_err_t esp_ble_gattc_write_char_descr (uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, +esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, uint16_t offset, @@ -300,10 +368,14 @@ esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_PREPARE_WRITE; - arg.prep_write.conn_id = conn_id; + arg.prep_write.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.prep_write.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.prep_write.char_id, char_id, sizeof(esp_gatt_id_t)); arg.prep_write.offset = offset; @@ -314,21 +386,25 @@ esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_execute_write (uint16_t conn_id, bool is_execute) +esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, bool is_execute) { btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_EXECUTE_WRITE; - arg.exec_write.conn_id = conn_id; + arg.exec_write.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); arg.exec_write.is_execute = is_execute; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, +esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, esp_bd_addr_t server_bda, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id) @@ -336,10 +412,14 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_REG_FOR_NOTIFY; - arg.reg_for_notify.gatt_if = gatt_if; + arg.reg_for_notify.gattc_if = gattc_if; memcpy(arg.reg_for_notify.remote_bda, server_bda, sizeof(esp_bd_addr_t)); memcpy(&arg.reg_for_notify.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.reg_for_notify.char_id, char_id, sizeof(esp_gatt_id_t)); @@ -347,7 +427,7 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gatt_if, +esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, esp_bd_addr_t server_bda, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id) @@ -355,10 +435,14 @@ esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gatt_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_UNREG_FOR_NOTIFY; - arg.unreg_for_notify.gatt_if = gatt_if; + arg.unreg_for_notify.gattc_if = gattc_if; memcpy(arg.unreg_for_notify.remote_bda, server_bda, sizeof(esp_bd_addr_t)); memcpy(&arg.unreg_for_notify.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.unreg_for_notify.char_id, char_id, sizeof(esp_gatt_id_t)); diff --git a/components/bt/bluedroid/api/esp_gatts_api.c b/components/bt/bluedroid/api/esp_gatts_api.c index 803ff030dd..f5ebe59a2d 100644 --- a/components/bt/bluedroid/api/esp_gatts_api.c +++ b/components/bt/bluedroid/api/esp_gatts_api.c @@ -15,13 +15,19 @@ #include "string.h" #include "esp_gatt_defs.h" #include "esp_gatts_api.h" +#include "esp_bt_main.h" #include "btc_manage.h" #include "btc_gatts.h" +#include "btc_gatt_util.h" #define COPY_TO_GATTS_ARGS(_gatt_args, _arg, _arg_type) memcpy(_gatt_args, _arg, sizeof(_arg_type)) -esp_err_t esp_ble_gatts_register_callback(esp_profile_cb_t callback) + +esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback) { + if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { + return ESP_ERR_INVALID_STATE; + } return (btc_profile_cb_set(BTC_PID_GATTS, callback) == 0 ? ESP_OK : ESP_FAIL); } @@ -30,6 +36,10 @@ esp_err_t esp_ble_gatts_app_register(uint16_t app_id) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + //if (app_id < ESP_APP_ID_MIN || app_id > ESP_APP_ID_MAX) { if (app_id > ESP_APP_ID_MAX) { return ESP_ERR_INVALID_ARG; @@ -44,41 +54,73 @@ esp_err_t esp_ble_gatts_app_register(uint16_t app_id) } -esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatt_if) +esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if) { btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_APP_UNREGISTER; - arg.app_unreg.gatt_if = gatt_if; + arg.app_unreg.gatts_if = gatts_if; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatt_if, +esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, esp_gatt_srvc_id_t *service_id, uint16_t num_handle) { btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CREATE_SERVICE; - arg.create_srvc.gatt_if = gatt_if; + arg.create_srvc.gatts_if = gatts_if; arg.create_srvc.num_handle = num_handle; memcpy(&arg.create_srvc.service_id, service_id, sizeof(esp_gatt_srvc_id_t)); return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } +esp_err_t esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t *gatts_attr_db, + esp_gatt_if_t gatts_if, + uint8_t max_nb_attr, + uint8_t srvc_inst_id) +{ + btc_msg_t msg; + btc_ble_gatts_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_CREATE_ATTR_TAB; + arg.create_attr_tab.gatts_if = gatts_if; + arg.create_attr_tab.max_nb_attr = max_nb_attr; + arg.create_attr_tab.srvc_inst_id = srvc_inst_id; + arg.create_attr_tab.gatts_attr_db = (esp_gatts_attr_db_t *)gatts_attr_db; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t included_service_handle) { btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_ADD_INCLUDE_SERVICE; @@ -90,38 +132,69 @@ esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t i esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_uuid, - esp_gatt_perm_t perm, esp_gatt_char_prop_t property) + esp_gatt_perm_t perm, esp_gatt_char_prop_t property, esp_attr_value_t *char_val, + esp_attr_control_t *control) { btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_ADD_CHAR; arg.add_char.service_handle = service_handle; arg.add_char.perm = perm; arg.add_char.property = property; + if (char_val != NULL) { + arg.add_char.char_val.attr_max_len = char_val->attr_max_len; + arg.add_char.char_val.attr_len = char_val->attr_len; + arg.add_char.char_val.attr_value = char_val->attr_value; + } + + if (control != NULL) { + arg.add_char.attr_control.auto_rsp = control->auto_rsp; + } memcpy(&arg.add_char.char_uuid, char_uuid, sizeof(esp_bt_uuid_t)); - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, esp_bt_uuid_t *descr_uuid, - esp_gatt_perm_t perm) + esp_gatt_perm_t perm, esp_attr_value_t *char_descr_val, + esp_attr_control_t *control) { btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_ADD_CHAR_DESCR; arg.add_descr.service_handle = service_handle; arg.add_descr.perm = perm; + + if (char_descr_val != NULL) { + arg.add_descr.descr_val.attr_max_len = char_descr_val->attr_max_len; + arg.add_descr.descr_val.attr_len = char_descr_val->attr_len; + arg.add_descr.descr_val.attr_value = char_descr_val->attr_value; + } + + if (control != NULL) { + arg.add_descr.attr_control.auto_rsp = control->auto_rsp; + } memcpy(&arg.add_descr.descr_uuid, descr_uuid, sizeof(esp_bt_uuid_t)); - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } esp_err_t esp_ble_gatts_delete_service(uint16_t service_handle) @@ -129,6 +202,10 @@ esp_err_t esp_ble_gatts_delete_service(uint16_t service_handle) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_DELETE_SERVICE; @@ -142,6 +219,10 @@ esp_err_t esp_ble_gatts_start_service(uint16_t service_handle) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_START_SERVICE; @@ -155,6 +236,10 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_STOP_SERVICE; @@ -164,65 +249,109 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle) } -esp_err_t esp_ble_gatts_send_indicate(uint16_t conn_id, uint16_t attr_handle, +esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t attr_handle, uint16_t value_len, uint8_t *value, bool need_confirm) { btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_SEND_INDICATE; - arg.send_ind.conn_id = conn_id; + arg.send_ind.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); arg.send_ind.attr_handle = attr_handle; arg.send_ind.need_confirm = need_confirm; arg.send_ind.value_len = value_len; arg.send_ind.value = value; - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), + btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gatts_send_response(uint16_t conn_id, uint32_t trans_id, +esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id, esp_gatt_status_t status, esp_gatt_rsp_t *rsp) { btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_SEND_RESPONSE; - arg.send_rsp.conn_id = conn_id; + arg.send_rsp.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); arg.send_rsp.trans_id = trans_id; arg.send_rsp.status = status; arg.send_rsp.rsp = rsp; - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), + btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bool is_direct) +esp_err_t esp_ble_gatts_set_attr_value(uint16_t attr_handle, uint16_t length, const uint8_t *value) { btc_msg_t msg; btc_ble_gatts_args_t arg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_SET_ATTR_VALUE; + arg.set_attr_val.length = length; + arg.set_attr_val.value = (uint8_t *)value; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), + btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value) +{ + if (attr_handle == ESP_GATT_ILLEGAL_HANDLE) { + return ESP_FAIL; + } + btc_gatts_get_attr_value(attr_handle, length, (uint8_t **)value); + return ESP_OK; +} + +esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, bool is_direct) +{ + btc_msg_t msg; + btc_ble_gatts_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_OPEN; - arg.open.gatt_if = gatt_if; + arg.open.gatts_if = gatts_if; arg.open.is_direct = is_direct; memcpy(&arg.open.remote_bda, remote_bda, sizeof(esp_bd_addr_t)); - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gatts_close(uint16_t conn_id) +esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id) { btc_msg_t msg; btc_ble_gatts_args_t arg; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CLOSE; - arg.close.conn_id = conn_id; + arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } diff --git a/components/bt/bluedroid/api/include/esp_a2dp_api.h b/components/bt/bluedroid/api/include/esp_a2dp_api.h index 8ff1a0cc6a..c8cc53e86a 100644 --- a/components/bt/bluedroid/api/include/esp_a2dp_api.h +++ b/components/bt/bluedroid/api/include/esp_a2dp_api.h @@ -118,6 +118,13 @@ typedef union { } audio_cfg; } esp_a2d_cb_param_t; +/** + * @brief A2DP profile callback function, data is ou + * @param event : Event type + * @param param : Point to callback parameter + */ +typedef void (* esp_a2d_cb_t)(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); + /** * @brief A2DP profile data callback function, data is ou * @@ -140,7 +147,7 @@ typedef void (* esp_a2d_data_cb_t)(const uint8_t *buf, uint32_t len); * - ESP_OK: success * */ -esp_err_t esp_a2d_register_callback(esp_profile_cb_t callback); +esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback); /** diff --git a/components/bt/bluedroid/api/include/esp_blufi_api.h b/components/bt/bluedroid/api/include/esp_blufi_api.h index 609d9c1c59..6da6512163 100644 --- a/components/bt/bluedroid/api/include/esp_blufi_api.h +++ b/components/bt/bluedroid/api/include/esp_blufi_api.h @@ -18,22 +18,44 @@ #include "esp_bt_defs.h" #include "esp_gatt_defs.h" #include "esp_err.h" +#include "esp_wifi_types.h" #ifdef __cplusplus extern "C" { #endif -#define ESP_BLUFI_RECV_DATA_LEN_MAX (64+1) - -#define ESP_BLUFI_EVENT_INIT_FINISH 0 -#define ESP_BLUFI_EVENT_DEINIT_FINISH 1 -#define ESP_BLUFI_EVENT_RECV_DATA 2 +typedef enum { + ESP_BLUFI_EVENT_INIT_FINISH = 0, /* -#include "osi.h" -#include "bt_common_types.h" -#include "esp_err.h" +#include -enum BLUFI_SIG { - BLUFI_SIG_SWITCH_CONTEXT = 0, - BLUFI_SIG_ENABLE, - BLUFI_SIG_DISABLE, -}; +#ifdef __cplusplus +extern "C" { +#endif -typedef esp_err_t (*blufi_task_cb_t)(void *arg); +/** + * + * @brief Get bluetooth device address. Must use after "esp_bluedroid_enable". + * + * @return bluetooth device address (six bytes), or NULL if bluetooth stack is not enabled + */ +const uint8_t *esp_bt_dev_get_address(void); -void blufi_init(void); -void blufi_deinit(void); +#ifdef __cplusplus +} +#endif -esp_err_t blufi_enable(void *arg); -esp_err_t blufi_disable(void *arg); -esp_err_t blufi_transfer_context(blufi_task_cb_t cb, void *arg); - -#endif /* __BT_APP_COMMON_H__ */ +#endif /* __ESP_BT_DEVICE_H__ */ diff --git a/components/bt/bluedroid/api/include/esp_bt_main.h b/components/bt/bluedroid/api/include/esp_bt_main.h index 859da092dd..fad010d2c2 100644 --- a/components/bt/bluedroid/api/include/esp_bt_main.h +++ b/components/bt/bluedroid/api/include/esp_bt_main.h @@ -22,22 +22,39 @@ extern "C" { #endif /** - * @brief Enable bluetooth, must after esp_init_bluetooth() - * - * @return - * - ESP_OK : Succeed - * - Other : Failed + * @brief Bluetooth stack status type, to indicate whether the bluetooth stack is ready */ -esp_err_t esp_enable_bluetooth(void); +typedef enum { + ESP_BLUEDROID_STATUS_UNINITIALIZED = 0, /*!< Bluetooth not initialized */ + ESP_BLUEDROID_STATUS_INITIALIZED, /*!< Bluetooth initialized but not enabled */ + ESP_BLUEDROID_STATUS_ENABLED /*!< Bluetooth initialized and enabled */ +} esp_bluedroid_status_t; /** - * @brief Disable bluetooth, must prior to esp_deinit_bluetooth() + * @brief Get bluetooth stack status + * + * @return Bluetooth stack status + * + */ +esp_bluedroid_status_t esp_bluedroid_get_status(void); + +/** + * @brief Enable bluetooth, must after esp_bluedroid_init() * * @return * - ESP_OK : Succeed * - Other : Failed */ -esp_err_t esp_disable_bluetooth(void); +esp_err_t esp_bluedroid_enable(void); + +/** + * @brief Disable bluetooth, must prior to esp_bluedroid_deinit() + * + * @return + * - ESP_OK : Succeed + * - Other : Failed + */ +esp_err_t esp_bluedroid_disable(void); /** * @brief Init and alloc the resource for bluetooth, must be prior to every bluetooth stuff @@ -46,7 +63,7 @@ esp_err_t esp_disable_bluetooth(void); * - ESP_OK : Succeed * - Other : Failed */ -esp_err_t esp_init_bluetooth(void); +esp_err_t esp_bluedroid_init(void); /** * @brief Deinit and free the resource for bluetooth, must be after every bluetooth stuff @@ -55,7 +72,7 @@ esp_err_t esp_init_bluetooth(void); * - ESP_OK : Succeed * - Other : Failed */ -esp_err_t esp_deinit_bluetooth(void); +esp_err_t esp_bluedroid_deinit(void); #ifdef __cplusplus } diff --git a/components/bt/bluedroid/api/include/esp_gap_ble_api.h b/components/bt/bluedroid/api/include/esp_gap_ble_api.h index d01595d595..1d27edb881 100644 --- a/components/bt/bluedroid/api/include/esp_gap_ble_api.h +++ b/components/bt/bluedroid/api/include/esp_gap_ble_api.h @@ -25,44 +25,59 @@ extern "C" { #endif +/**@{ + * BLE_ADV_DATA_FLAG data flag bit definition used for advertising data flag + */ +#define ESP_BLE_ADV_FLAG_LIMIT_DISC (0x01 << 0) +#define ESP_BLE_ADV_FLAG_GEN_DISC (0x01 << 1) +#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT (0x01 << 2) +#define ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) +#define ESP_BLE_ADV_FLAG_DMT_HOST_SPT (0x01 << 4) +#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) +/** + * @} + */ + /// GAP BLE callback event type typedef enum { - ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT = 0, /*!< When advertising data set complete, the event comes */ - ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT , /*!< When scan response data set complete, the event comes */ - ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, /*!< When scan parameters set complete, the event comes */ - ESP_GAP_BLE_SCAN_RESULT_EVT, /*!< When one scan result ready, the event comes each time */ -}esp_gap_ble_cb_event_t; + ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT = 0, /*!< When advertising data set complete, the event comes */ + ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT, /*!< When scan response data set complete, the event comes */ + ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, /*!< When scan parameters set complete, the event comes */ + ESP_GAP_BLE_SCAN_RESULT_EVT, /*!< When one scan result ready, the event comes each time */ +} esp_gap_ble_cb_event_t; /// Advertising data maximum length -#define ESP_BLE_ADV_DATA_LEN_MAX 31 +#define ESP_BLE_ADV_DATA_LEN_MAX 31 +/// Scan response data maximum length +#define ESP_BLE_SCAN_RSP_DATA_LEN_MAX 31 /// The type of advertising data(not adv_type) typedef enum { - ESP_BLE_AD_TYPE_FLAG = 0x01, - ESP_BLE_AD_TYPE_16SRV_PART = 0x02, - ESP_BLE_AD_TYPE_16SRV_CMPL = 0x03, - ESP_BLE_AD_TYPE_32SRV_PART = 0x04, - ESP_BLE_AD_TYPE_32SRV_CMPL = 0x05, - ESP_BLE_AD_TYPE_128SRV_PART = 0x06, - ESP_BLE_AD_TYPE_128SRV_CMPL = 0x07, - ESP_BLE_AD_TYPE_NAME_SHORT = 0x08, - ESP_BLE_AD_TYPE_NAME_CMPL = 0x09, - ESP_BLE_AD_TYPE_TX_PWR = 0x0A, - ESP_BLE_AD_TYPE_DEV_CLASS = 0x0D, - ESP_BLE_AD_TYPE_SM_TK = 0x10, - ESP_BLE_AD_TYPE_SM_OOB_FLAG = 0x11, - ESP_BLE_AD_TYPE_INT_RANGE = 0x12, - ESP_BLE_AD_TYPE_SOL_SRV_UUID = 0x14, - ESP_BLE_AD_TYPE_128SOL_SRV_UUID = 0x15, - ESP_BLE_AD_TYPE_SERVICE_DATA = 0x16, - ESP_BLE_AD_TYPE_PUBLIC_TARGET = 0x17, - ESP_BLE_AD_TYPE_RANDOM_TARGET = 0x18, - ESP_BLE_AD_TYPE_APPEARANCE = 0x19, - ESP_BLE_AD_TYPE_ADV_INT = 0x1A, - ESP_BLE_AD_TYPE_32SOL_SRV_UUID = 0x1B, - ESP_BLE_AD_TYPE_32SERVICE_DATA = 0x1C, - ESP_BLE_AD_TYPE_128SERVICE_DATA = 0x1D, - ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE = 0xFF, + ESP_BLE_AD_TYPE_FLAG = 0x01, + ESP_BLE_AD_TYPE_16SRV_PART = 0x02, + ESP_BLE_AD_TYPE_16SRV_CMPL = 0x03, + ESP_BLE_AD_TYPE_32SRV_PART = 0x04, + ESP_BLE_AD_TYPE_32SRV_CMPL = 0x05, + ESP_BLE_AD_TYPE_128SRV_PART = 0x06, + ESP_BLE_AD_TYPE_128SRV_CMPL = 0x07, + ESP_BLE_AD_TYPE_NAME_SHORT = 0x08, + ESP_BLE_AD_TYPE_NAME_CMPL = 0x09, + ESP_BLE_AD_TYPE_TX_PWR = 0x0A, + ESP_BLE_AD_TYPE_DEV_CLASS = 0x0D, + ESP_BLE_AD_TYPE_SM_TK = 0x10, + ESP_BLE_AD_TYPE_SM_OOB_FLAG = 0x11, + ESP_BLE_AD_TYPE_INT_RANGE = 0x12, + ESP_BLE_AD_TYPE_SOL_SRV_UUID = 0x14, + ESP_BLE_AD_TYPE_128SOL_SRV_UUID = 0x15, + ESP_BLE_AD_TYPE_SERVICE_DATA = 0x16, + ESP_BLE_AD_TYPE_PUBLIC_TARGET = 0x17, + ESP_BLE_AD_TYPE_RANDOM_TARGET = 0x18, + ESP_BLE_AD_TYPE_APPEARANCE = 0x19, + ESP_BLE_AD_TYPE_ADV_INT = 0x1A, + ESP_BLE_AD_TYPE_32SOL_SRV_UUID = 0x1B, + ESP_BLE_AD_TYPE_32SERVICE_DATA = 0x1C, + ESP_BLE_AD_TYPE_128SERVICE_DATA = 0x1D, + ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE = 0xFF, } esp_ble_adv_data_type; /// Advertising mode @@ -96,37 +111,37 @@ typedef enum { /// Advertising parameters typedef struct { - uint16_t adv_int_min; /*!< Minimum advertising interval for - undirected and low duty cycle directed advertising. - Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) - Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec */ - uint16_t adv_int_max; /*!< Maximum advertising interval for - undirected and low duty cycle directed advertising. - Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) - Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec Advertising max interval */ - esp_ble_adv_type_t adv_type; /*!< Advertising type */ - esp_ble_addr_type_t own_addr_type; /*!< Owner bluetooth device address type */ - esp_bd_addr_t peer_addr; /*!< Peer device bluetooth device address */ - esp_ble_addr_type_t peer_addr_type; /*!< Peer device bluetooth device address type */ - esp_ble_adv_channel_t channel_map; /*!< Advertising channel map */ - esp_ble_adv_filter_t adv_filter_policy; /*!< Advertising filter policy */ + uint16_t adv_int_min; /*!< Minimum advertising interval for + undirected and low duty cycle directed advertising. + Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) + Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec */ + uint16_t adv_int_max; /*!< Maximum advertising interval for + undirected and low duty cycle directed advertising. + Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) + Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec Advertising max interval */ + esp_ble_adv_type_t adv_type; /*!< Advertising type */ + esp_ble_addr_type_t own_addr_type; /*!< Owner bluetooth device address type */ + esp_bd_addr_t peer_addr; /*!< Peer device bluetooth device address */ + esp_ble_addr_type_t peer_addr_type; /*!< Peer device bluetooth device address type */ + esp_ble_adv_channel_t channel_map; /*!< Advertising channel map */ + esp_ble_adv_filter_t adv_filter_policy; /*!< Advertising filter policy */ } esp_ble_adv_params_t; /// Advertising data content, according to "Supplement to the Bluetooth Core Specification" typedef struct { - bool set_scan_rsp; /*!< Set this advertising data as scan response or not*/ - bool include_name; /*!< Advertising data include device name or not */ - bool include_txpower; /*!< Advertising data include TX power */ - int min_interval; /*!< Advertising data show advertising min interval */ - int max_interval; /*!< Advertising data show advertising max interval */ - int appearance; /*!< External appearance of device */ - uint16_t manufacturer_len; /*!< Manufacturer data length */ - uint8_t *p_manufacturer_data; /*!< Manufacturer data point */ - uint16_t service_data_len; /*!< Service data length */ - uint8_t *p_service_data; /*!< Service data point */ - uint16_t service_uuid_len; /*!< Service uuid length */ - uint8_t *p_service_uuid; /*!< Service uuid array point */ - uint8_t flag; /*!< Advertising flag of discovery mode */ + bool set_scan_rsp; /*!< Set this advertising data as scan response or not*/ + bool include_name; /*!< Advertising data include device name or not */ + bool include_txpower; /*!< Advertising data include TX power */ + int min_interval; /*!< Advertising data show advertising min interval */ + int max_interval; /*!< Advertising data show advertising max interval */ + int appearance; /*!< External appearance of device */ + uint16_t manufacturer_len; /*!< Manufacturer data length */ + uint8_t *p_manufacturer_data; /*!< Manufacturer data point */ + uint16_t service_data_len; /*!< Service data length */ + uint8_t *p_service_data; /*!< Service data point */ + uint16_t service_uuid_len; /*!< Service uuid length */ + uint8_t *p_service_uuid; /*!< Service uuid array point */ + uint8_t flag; /*!< Advertising flag of discovery mode, see BLE_ADV_DATA_FLAG detail */ } esp_ble_adv_data_t; /// Own BD address source of the device @@ -147,53 +162,53 @@ typedef enum { /// Ble scan type typedef enum { - BLE_SCAN_TYPE_PASSIVE = 0x0, /*!< Passive scan */ - BLE_SCAN_TYPE_ACTIVE = 0x1, /*!< Active scan */ + BLE_SCAN_TYPE_PASSIVE = 0x0, /*!< Passive scan */ + BLE_SCAN_TYPE_ACTIVE = 0x1, /*!< Active scan */ } esp_ble_scan_type_t; /// Ble scan filter type typedef enum { - BLE_SCAN_FILTER_ALLOW_ALL = 0x0, /*!< Accept all : - 1. advertisement packets except directed advertising packets not addressed to this device (default). */ + BLE_SCAN_FILTER_ALLOW_ALL = 0x0, /*!< Accept all : + 1. advertisement packets except directed advertising packets not addressed to this device (default). */ BLE_SCAN_FILTER_ALLOW_ONLY_WLST = 0x1, /*!< Accept only : - 1. advertisement packets from devices where the advertiser’s address is in the White list. - 2. Directed advertising packets which are not addressed for this device shall be ignored. */ + 1. advertisement packets from devices where the advertiser’s address is in the White list. + 2. Directed advertising packets which are not addressed for this device shall be ignored. */ BLE_SCAN_FILTER_ALLOW_UND_RPA_DIR = 0x2, /*!< Accept all : - 1. undirected advertisement packets, and - 2. directed advertising packets where the initiator address is a resolvable private address, and - 3. directed advertising packets addressed to this device. */ + 1. undirected advertisement packets, and + 2. directed advertising packets where the initiator address is a resolvable private address, and + 3. directed advertising packets addressed to this device. */ BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR = 0x3, /*!< Accept all : - 1. advertisement packets from devices where the advertiser’s address is in the White list, and - 2. directed advertising packets where the initiator address is a resolvable private address, and - 3. directed advertising packets addressed to this device.*/ + 1. advertisement packets from devices where the advertiser’s address is in the White list, and + 2. directed advertising packets where the initiator address is a resolvable private address, and + 3. directed advertising packets addressed to this device.*/ } esp_ble_scan_filter_t; /// Ble scan parameters typedef struct { - esp_ble_scan_type_t scan_type; /*!< Scan type */ - esp_ble_addr_type_t own_addr_type; /*!< Owner address type */ - esp_ble_scan_filter_t scan_filter_policy; /*!< Scan filter policy */ - uint16_t scan_interval; /*!< Scan interval. This is defined as the time interval from - when the Controller started its last LE scan until it begins the subsequent LE scan. - Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) - Time = N * 0.625 msec - Time Range: 2.5 msec to 10.24 seconds*/ - uint16_t scan_window; /*!< Scan window. The duration of the LE scan. LE_Scan_Window - shall be less than or equal to LE_Scan_Interval - Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) - Time = N * 0.625 msec - Time Range: 2.5 msec to 10240 msec */ + esp_ble_scan_type_t scan_type; /*!< Scan type */ + esp_ble_addr_type_t own_addr_type; /*!< Owner address type */ + esp_ble_scan_filter_t scan_filter_policy; /*!< Scan filter policy */ + uint16_t scan_interval; /*!< Scan interval. This is defined as the time interval from + when the Controller started its last LE scan until it begins the subsequent LE scan. + Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) + Time = N * 0.625 msec + Time Range: 2.5 msec to 10.24 seconds*/ + uint16_t scan_window; /*!< Scan window. The duration of the LE scan. LE_Scan_Window + shall be less than or equal to LE_Scan_Interval + Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) + Time = N * 0.625 msec + Time Range: 2.5 msec to 10240 msec */ } esp_ble_scan_params_t; /// Connection update parameters typedef struct { - esp_bd_addr_t bda; /*!< Bluetooth device address */ - uint16_t min_int; /*!< Min connection interval */ - uint16_t max_int; /*!< Max connection interval */ - uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ - uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. - Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec - Time Range: 100 msec to 32 seconds */ + esp_bd_addr_t bda; /*!< Bluetooth device address */ + uint16_t min_int; /*!< Min connection interval */ + uint16_t max_int; /*!< Max connection interval */ + uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ + uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. + Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec + Time Range: 100 msec to 32 seconds */ } esp_ble_conn_update_params_t; /// Sub Event of ESP_GAP_BLE_SCAN_RESULT_EVT @@ -212,11 +227,11 @@ typedef enum { * result is scan response or advertising data or other */ typedef enum { - ESP_BLE_EVT_CONN_ADV = 0x00, /*!< Connectable undirected advertising (ADV_IND) */ - ESP_BLE_EVT_CONN_DIR_ADV = 0x01, /*!< Connectable directed advertising (ADV_DIRECT_IND) */ - ESP_BLE_EVT_DISC_ADV = 0x02, /*!< Scannable undirected advertising (ADV_SCAN_IND) */ - ESP_BLE_EVT_NON_CONN_ADV = 0x03, /*!< Non connectable undirected advertising (ADV_NONCONN_IND) */ - ESP_BLE_EVT_SCAN_RSP = 0x04, /*!< Scan Response (SCAN_RSP) */ + ESP_BLE_EVT_CONN_ADV = 0x00, /*!< Connectable undirected advertising (ADV_IND) */ + ESP_BLE_EVT_CONN_DIR_ADV = 0x01, /*!< Connectable directed advertising (ADV_DIRECT_IND) */ + ESP_BLE_EVT_DISC_ADV = 0x02, /*!< Scannable undirected advertising (ADV_SCAN_IND) */ + ESP_BLE_EVT_NON_CONN_ADV = 0x03, /*!< Non connectable undirected advertising (ADV_NONCONN_IND) */ + ESP_BLE_EVT_SCAN_RSP = 0x04, /*!< Scan Response (SCAN_RSP) */ } esp_ble_evt_type_t; /** @@ -227,36 +242,43 @@ typedef union { * @brief ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT */ struct ble_adv_data_cmpl_evt_param { - esp_bt_status_t status; /*!< Indicate the set advertising data operation success status */ - } adv_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT */ + esp_bt_status_t status; /*!< Indicate the set advertising data operation success status */ + } adv_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT */ /** * @brief ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT */ struct ble_scan_rsp_data_cmpl_evt_param { - esp_bt_status_t status; /*!< Indicate the set scan response data operation success status */ - } scan_rsp_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT */ + esp_bt_status_t status; /*!< Indicate the set scan response data operation success status */ + } scan_rsp_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT */ /** * @brief ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT */ struct ble_scan_param_cmpl_evt_param { - esp_bt_status_t status; /*!< Indicate the set scan param operation success status */ - } scan_param_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT */ + esp_bt_status_t status; /*!< Indicate the set scan param operation success status */ + } scan_param_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT */ /** * @brief ESP_GAP_BLE_SCAN_RESULT_EVT */ struct ble_scan_result_evt_param { - esp_gap_search_evt_t search_evt; /*!< Search event type */ - esp_bd_addr_t bda; /*!< Bluetooth device address which has been searched */ - esp_bt_dev_type_t dev_type; /*!< Device type */ - esp_ble_addr_type_t ble_addr_type; /*!< Ble device address type */ - esp_ble_evt_type_t ble_evt_type; /*!< Ble scan result event type */ - int rssi; /*!< Searched device's RSSI */ - uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX]; /*!< Received EIR */ - int flag; /*!< Advertising data flag bit */ - int num_resps; /*!< Scan result number */ - } scan_rst; /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */ + esp_gap_search_evt_t search_evt; /*!< Search event type */ + esp_bd_addr_t bda; /*!< Bluetooth device address which has been searched */ + esp_bt_dev_type_t dev_type; /*!< Device type */ + esp_ble_addr_type_t ble_addr_type; /*!< Ble device address type */ + esp_ble_evt_type_t ble_evt_type; /*!< Ble scan result event type */ + int rssi; /*!< Searched device's RSSI */ + uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; /*!< Received EIR */ + int flag; /*!< Advertising data flag bit */ + int num_resps; /*!< Scan result number */ + } scan_rst; /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */ } esp_ble_gap_cb_param_t; +/** + * @brief GAP callback function type + * @param event : Event type + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_gap_ble_cb_t)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); + /** * @brief This function is called to occur gap event, such as scan result * @@ -267,7 +289,7 @@ typedef union { * - other : failed * */ -esp_err_t esp_ble_gap_register_callback(esp_profile_cb_t callback); +esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback); /** @@ -420,7 +442,8 @@ esp_err_t esp_ble_gap_set_device_name(const char *name); * @param[in] type - finding ADV data type * @param[out] length - return the length of ADV data not including type * - * @return pointer of ADV data + * @return - ESP_OK : success + * - other : failed * */ uint8_t *esp_ble_resolve_adv_data(uint8_t *adv_data, uint8_t type, uint8_t *length); diff --git a/components/bt/bluedroid/api/include/esp_gatt_defs.h b/components/bt/bluedroid/api/include/esp_gatt_defs.h index 931733e402..153a702735 100644 --- a/components/bt/bluedroid/api/include/esp_gatt_defs.h +++ b/components/bt/bluedroid/api/include/esp_gatt_defs.h @@ -22,106 +22,141 @@ extern "C" { #endif /// GATT INVALID UUID -#define ESP_GATT_ILLEGAL_UUID 0 +#define ESP_GATT_ILLEGAL_UUID 0 +/// GATT INVALID HANDLE +#define ESP_GATT_ILLEGAL_HANDLE 0 +/// GATT attribute max handle +#define ESP_GATT_ATTR_HANDLE_MAX 100 + /**@{ * All "ESP_GATT_UUID_xxx" is attribute types */ -#define ESP_GATT_UUID_PRI_SERVICE 0x2800 -#define ESP_GATT_UUID_SEC_SERVICE 0x2801 -#define ESP_GATT_UUID_INCLUDE_SERVICE 0x2802 -#define ESP_GATT_UUID_CHAR_DECLARE 0x2803 /* Characteristic Declaration*/ +#define ESP_GATT_UUID_IMMEDIATE_ALERT_SVC 0x1802 /* Immediate alert Service*/ +#define ESP_GATT_UUID_LINK_LOSS_SVC 0x1803 /* Link Loss Service*/ +#define ESP_GATT_UUID_TX_POWER_SVC 0x1804 /* TX Power Service*/ +#define ESP_GATT_UUID_CURRENT_TIME_SVC 0x1805 /* Current Time Service Service*/ +#define ESP_GATT_UUID_REF_TIME_UPDATE_SVC 0x1806 /* Reference Time Update Service*/ +#define ESP_GATT_UUID_NEXT_DST_CHANGE_SVC 0x1807 /* Next DST Change Service*/ +#define ESP_GATT_UUID_GLUCOSE_SVC 0x1808 /* Glucose Service*/ +#define ESP_GATT_UUID_HEALTH_THERMOM_SVC 0x1809 /* Health Thermometer Service*/ +#define ESP_GATT_UUID_DEVICE_INFO_SVC 0x180A /* Device Information Service*/ +#define ESP_GATT_UUID_HEART_RATE_SVC 0x180D /* Heart Rate Service*/ +#define ESP_GATT_UUID_PHONE_ALERT_STATUS_SVC 0x180E /* Phone Alert Status Service*/ +#define ESP_GATT_UUID_BATTERY_SERVICE_SVC 0x180F /* Battery Service*/ +#define ESP_GATT_UUID_BLOOD_PRESSURE_SVC 0x1810 /* Blood Pressure Service*/ +#define ESP_GATT_UUID_ALERT_NTF_SVC 0x1811 /* Alert Notification Service*/ +#define ESP_GATT_UUID_HID_SVC 0x1812 /* HID Service*/ +#define ESP_GATT_UUID_SCAN_PARAMETERS_SVC 0x1813 /* Scan Parameters Service*/ +#define ESP_GATT_UUID_RUNNING_SPEED_CADENCE_SVC 0x1814 /* Running Speed and Cadence Service*/ +#define ESP_GATT_UUID_CYCLING_SPEED_CADENCE_SVC 0x1816 /* Cycling Speed and Cadence Service*/ +#define ESP_GATT_UUID_CYCLING_POWER_SVC 0x1818 /* Cycling Power Service*/ +#define ESP_GATT_UUID_LOCATION_AND_NAVIGATION_SVC 0x1819 /* Location and Navigation Service*/ +#define ESP_GATT_UUID_USER_DATA_SVC 0x181C /* User Data Service*/ +#define ESP_GATT_UUID_WEIGHT_SCALE_SVC 0x181D /* Weight Scale Service*/ -#define ESP_GATT_UUID_CHAR_EXT_PROP 0x2900 /* Characteristic Extended Properties */ -#define ESP_GATT_UUID_CHAR_DESCRIPTION 0x2901 /* Characteristic User Description*/ -#define ESP_GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */ -#define ESP_GATT_UUID_CHAR_SRVR_CONFIG 0x2903 /* Server Characteristic Configuration */ -#define ESP_GATT_UUID_CHAR_PRESENT_FORMAT 0x2904 /* Characteristic Presentation Format*/ -#define ESP_GATT_UUID_CHAR_AGG_FORMAT 0x2905 /* Characteristic Aggregate Format*/ -#define ESP_GATT_UUID_CHAR_VALID_RANGE 0x2906 /* Characteristic Valid Range */ -#define ESP_GATT_UUID_EXT_RPT_REF_DESCR 0x2907 -#define ESP_GATT_UUID_RPT_REF_DESCR 0x2908 +#define ESP_GATT_UUID_PRI_SERVICE 0x2800 +#define ESP_GATT_UUID_SEC_SERVICE 0x2801 +#define ESP_GATT_UUID_INCLUDE_SERVICE 0x2802 +#define ESP_GATT_UUID_CHAR_DECLARE 0x2803 /* Characteristic Declaration*/ + +#define ESP_GATT_UUID_CHAR_EXT_PROP 0x2900 /* Characteristic Extended Properties */ +#define ESP_GATT_UUID_CHAR_DESCRIPTION 0x2901 /* Characteristic User Description*/ +#define ESP_GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */ +#define ESP_GATT_UUID_CHAR_SRVR_CONFIG 0x2903 /* Server Characteristic Configuration */ +#define ESP_GATT_UUID_CHAR_PRESENT_FORMAT 0x2904 /* Characteristic Presentation Format*/ +#define ESP_GATT_UUID_CHAR_AGG_FORMAT 0x2905 /* Characteristic Aggregate Format*/ +#define ESP_GATT_UUID_CHAR_VALID_RANGE 0x2906 /* Characteristic Valid Range */ +#define ESP_GATT_UUID_EXT_RPT_REF_DESCR 0x2907 +#define ESP_GATT_UUID_RPT_REF_DESCR 0x2908 /* GAP Profile Attributes */ -#define ESP_GATT_UUID_GAP_DEVICE_NAME 0x2A00 -#define ESP_GATT_UUID_GAP_ICON 0x2A01 -#define ESP_GATT_UUID_GAP_PREF_CONN_PARAM 0x2A04 -#define ESP_GATT_UUID_GAP_CENTRAL_ADDR_RESOL 0x2AA6 +#define ESP_GATT_UUID_GAP_DEVICE_NAME 0x2A00 +#define ESP_GATT_UUID_GAP_ICON 0x2A01 +#define ESP_GATT_UUID_GAP_PREF_CONN_PARAM 0x2A04 +#define ESP_GATT_UUID_GAP_CENTRAL_ADDR_RESOL 0x2AA6 /* Attribute Profile Attribute UUID */ -#define ESP_GATT_UUID_GATT_SRV_CHGD 0x2A05 +#define ESP_GATT_UUID_GATT_SRV_CHGD 0x2A05 /* Link ESP_Loss Service */ -#define ESP_GATT_UUID_ALERT_LEVEL 0x2A06 /* Alert Level */ -#define ESP_GATT_UUID_TX_POWER_LEVEL 0x2A07 /* TX power level */ +#define ESP_GATT_UUID_ALERT_LEVEL 0x2A06 /* Alert Level */ +#define ESP_GATT_UUID_TX_POWER_LEVEL 0x2A07 /* TX power level */ /* Current Time Service */ -#define ESP_GATT_UUID_CURRENT_TIME 0x2A2B /* Current Time */ -#define ESP_GATT_UUID_LOCAL_TIME_INFO 0x2A0F /* Local time info */ -#define ESP_GATT_UUID_REF_TIME_INFO 0x2A14 /* reference time information */ +#define ESP_GATT_UUID_CURRENT_TIME 0x2A2B /* Current Time */ +#define ESP_GATT_UUID_LOCAL_TIME_INFO 0x2A0F /* Local time info */ +#define ESP_GATT_UUID_REF_TIME_INFO 0x2A14 /* reference time information */ /* Network availability Profile */ -#define ESP_GATT_UUID_NW_STATUS 0x2A18 /* network availability status */ -#define ESP_GATT_UUID_NW_TRIGGER 0x2A1A /* Network availability trigger */ +#define ESP_GATT_UUID_NW_STATUS 0x2A18 /* network availability status */ +#define ESP_GATT_UUID_NW_TRIGGER 0x2A1A /* Network availability trigger */ /* Phone alert */ -#define ESP_GATT_UUID_ALERT_STATUS 0x2A3F /* alert status */ -#define ESP_GATT_UUID_RINGER_CP 0x2A40 /* ringer control point */ -#define ESP_GATT_UUID_RINGER_SETTING 0x2A41 /* ringer setting */ +#define ESP_GATT_UUID_ALERT_STATUS 0x2A3F /* alert status */ +#define ESP_GATT_UUID_RINGER_CP 0x2A40 /* ringer control point */ +#define ESP_GATT_UUID_RINGER_SETTING 0x2A41 /* ringer setting */ /* Glucose Service */ -#define ESP_GATT_UUID_GM_MEASUREMENT 0x2A18 -#define ESP_GATT_UUID_GM_CONTEXT 0x2A34 -#define ESP_GATT_UUID_GM_CONTROL_POINT 0x2A52 -#define ESP_GATT_UUID_GM_FEATURE 0x2A51 +#define ESP_GATT_UUID_GM_MEASUREMENT 0x2A18 +#define ESP_GATT_UUID_GM_CONTEXT 0x2A34 +#define ESP_GATT_UUID_GM_CONTROL_POINT 0x2A52 +#define ESP_GATT_UUID_GM_FEATURE 0x2A51 /* device information characteristic */ -#define ESP_GATT_UUID_SYSTEM_ID 0x2A23 -#define ESP_GATT_UUID_MODEL_NUMBER_STR 0x2A24 -#define ESP_GATT_UUID_SERIAL_NUMBER_STR 0x2A25 -#define ESP_GATT_UUID_FW_VERSION_STR 0x2A26 -#define ESP_GATT_UUID_HW_VERSION_STR 0x2A27 -#define ESP_GATT_UUID_SW_VERSION_STR 0x2A28 -#define ESP_GATT_UUID_MANU_NAME 0x2A29 -#define ESP_GATT_UUID_IEEE_DATA 0x2A2A -#define ESP_GATT_UUID_PNP_ID 0x2A50 +#define ESP_GATT_UUID_SYSTEM_ID 0x2A23 +#define ESP_GATT_UUID_MODEL_NUMBER_STR 0x2A24 +#define ESP_GATT_UUID_SERIAL_NUMBER_STR 0x2A25 +#define ESP_GATT_UUID_FW_VERSION_STR 0x2A26 +#define ESP_GATT_UUID_HW_VERSION_STR 0x2A27 +#define ESP_GATT_UUID_SW_VERSION_STR 0x2A28 +#define ESP_GATT_UUID_MANU_NAME 0x2A29 +#define ESP_GATT_UUID_IEEE_DATA 0x2A2A +#define ESP_GATT_UUID_PNP_ID 0x2A50 /* HID characteristics */ -#define ESP_GATT_UUID_HID_INFORMATION 0x2A4A -#define ESP_GATT_UUID_HID_REPORT_MAP 0x2A4B -#define ESP_GATT_UUID_HID_CONTROL_POINT 0x2A4C -#define ESP_GATT_UUID_HID_REPORT 0x2A4D -#define ESP_GATT_UUID_HID_PROTO_MODE 0x2A4E -#define ESP_GATT_UUID_HID_BT_KB_INPUT 0x2A22 -#define ESP_GATT_UUID_HID_BT_KB_OUTPUT 0x2A32 -#define ESP_GATT_UUID_HID_BT_MOUSE_INPUT 0x2A33 +#define ESP_GATT_UUID_HID_INFORMATION 0x2A4A +#define ESP_GATT_UUID_HID_REPORT_MAP 0x2A4B +#define ESP_GATT_UUID_HID_CONTROL_POINT 0x2A4C +#define ESP_GATT_UUID_HID_REPORT 0x2A4D +#define ESP_GATT_UUID_HID_PROTO_MODE 0x2A4E +#define ESP_GATT_UUID_HID_BT_KB_INPUT 0x2A22 +#define ESP_GATT_UUID_HID_BT_KB_OUTPUT 0x2A32 +#define ESP_GATT_UUID_HID_BT_MOUSE_INPUT 0x2A33 + + /// Heart Rate Measurement +#define ESP_GATT_HEART_RATE_MEAS 0x2A37 +/// Body Sensor Location +#define ESP_GATT_BODY_SENSOR_LOCATION 0x2A38 +/// Heart Rate Control Point +#define ESP_GATT_HEART_RATE_CNTL_POINT 0x2A39 /* Battery Service characteristics */ -#define ESP_GATT_UUID_BATTERY_LEVEL 0x2A19 +#define ESP_GATT_UUID_BATTERY_LEVEL 0x2A19 /* Sensor Service */ -#define ESP_GATT_UUID_SC_CONTROL_POINT 0x2A55 -#define ESP_GATT_UUID_SENSOR_LOCATION 0x2A5D +#define ESP_GATT_UUID_SC_CONTROL_POINT 0x2A55 +#define ESP_GATT_UUID_SENSOR_LOCATION 0x2A5D /* Runners speed and cadence service */ -#define ESP_GATT_UUID_RSC_MEASUREMENT 0x2A53 -#define ESP_GATT_UUID_RSC_FEATURE 0x2A54 +#define ESP_GATT_UUID_RSC_MEASUREMENT 0x2A53 +#define ESP_GATT_UUID_RSC_FEATURE 0x2A54 /* Cycling speed and cadence service */ -#define ESP_GATT_UUID_CSC_MEASUREMENT 0x2A5B -#define ESP_GATT_UUID_CSC_FEATURE 0x2A5C +#define ESP_GATT_UUID_CSC_MEASUREMENT 0x2A5B +#define ESP_GATT_UUID_CSC_FEATURE 0x2A5C /* Scan ESP_Parameter characteristics */ -#define ESP_GATT_UUID_SCAN_INT_WINDOW 0x2A4F -#define ESP_GATT_UUID_SCAN_REFRESH 0x2A31 +#define ESP_GATT_UUID_SCAN_INT_WINDOW 0x2A4F +#define ESP_GATT_UUID_SCAN_REFRESH 0x2A31 /** * @} */ /// Attribute write data type from the client typedef enum { - ESP_GATT_PREP_WRITE_CANCEL = 0x00, /*!< Prepare write cancel */ - ESP_GATT_PREP_WRITE_EXEC = 0x01, /*!< Prepare write execute */ + ESP_GATT_PREP_WRITE_CANCEL = 0x00, /*!< Prepare write cancel */ + ESP_GATT_PREP_WRITE_EXEC = 0x01, /*!< Prepare write execute */ } esp_gatt_prep_write_type; /** @@ -178,23 +213,23 @@ typedef enum { * @brief Gatt Connection reason enum */ typedef enum { - ESP_GATT_CONN_UNKNOWN = 0, /*!< Gatt connection unknown */ - ESP_GATT_CONN_L2C_FAILURE = 1, /*!< General L2cap failure */ - ESP_GATT_CONN_TIMEOUT = 0x08, /*!< Connection timeout */ - ESP_GATT_CONN_TERMINATE_PEER_USER = 0x13, /*!< Connection terminate by peer user */ - ESP_GATT_CONN_TERMINATE_LOCAL_HOST = 0x16, /*!< Connectionterminated by local host */ - ESP_GATT_CONN_FAIL_ESTABLISH = 0x3e, /*!< Connection fail to establish */ - ESP_GATT_CONN_LMP_TIMEOUT = 0x22, /*!< Connection fail for LMP response tout */ - ESP_GATT_CONN_CONN_CANCEL = 0x0100, /*!< L2CAP connection cancelled */ - ESP_GATT_CONN_NONE = 0x0101 /*!< No connection to cancel */ + ESP_GATT_CONN_UNKNOWN = 0, /*!< Gatt connection unknown */ + ESP_GATT_CONN_L2C_FAILURE = 1, /*!< General L2cap failure */ + ESP_GATT_CONN_TIMEOUT = 0x08, /*!< Connection timeout */ + ESP_GATT_CONN_TERMINATE_PEER_USER = 0x13, /*!< Connection terminate by peer user */ + ESP_GATT_CONN_TERMINATE_LOCAL_HOST = 0x16, /*!< Connectionterminated by local host */ + ESP_GATT_CONN_FAIL_ESTABLISH = 0x3e, /*!< Connection fail to establish */ + ESP_GATT_CONN_LMP_TIMEOUT = 0x22, /*!< Connection fail for LMP response tout */ + ESP_GATT_CONN_CONN_CANCEL = 0x0100, /*!< L2CAP connection cancelled */ + ESP_GATT_CONN_NONE = 0x0101 /*!< No connection to cancel */ } esp_gatt_conn_reason_t; /** * @brief Gatt id, include uuid and instance id */ typedef struct { - esp_bt_uuid_t uuid; /*!< UUID */ - uint8_t inst_id; /*!< Instance id */ + esp_bt_uuid_t uuid; /*!< UUID */ + uint8_t inst_id; /*!< Instance id */ } __attribute__((packed)) esp_gatt_id_t; /** @@ -202,19 +237,19 @@ typedef struct { * (uuid and instance id) and primary flag */ typedef struct { - esp_gatt_id_t id; /*!< Gatt id, include uuid and instance */ - bool is_primary; /*!< This service is primary or not */ + esp_gatt_id_t id; /*!< Gatt id, include uuid and instance */ + bool is_primary; /*!< This service is primary or not */ } __attribute__((packed)) esp_gatt_srvc_id_t; /** * @brief Gatt authentication request type */ typedef enum { - ESP_GATT_AUTH_REQ_NONE = 0, - ESP_GATT_AUTH_REQ_NO_MITM = 1, /* unauthenticated encryption */ - ESP_GATT_AUTH_REQ_MITM = 2, /* authenticated encryption */ - ESP_GATT_AUTH_REQ_SIGNED_NO_MITM = 3, - ESP_GATT_AUTH_REQ_SIGNED_MITM = 4, + ESP_GATT_AUTH_REQ_NONE = 0, + ESP_GATT_AUTH_REQ_NO_MITM = 1, /* unauthenticated encryption */ + ESP_GATT_AUTH_REQ_MITM = 2, /* authenticated encryption */ + ESP_GATT_AUTH_REQ_SIGNED_NO_MITM = 3, + ESP_GATT_AUTH_REQ_SIGNED_MITM = 4, } esp_gatt_auth_req_t; /** @@ -246,30 +281,101 @@ typedef enum { /// GATT maximum attribute length #define ESP_GATT_MAX_ATTR_LEN 600 //as same as GATT_MAX_ATTR_LEN + +/** + * @brief Attribute description (used to create database) + */ + typedef struct + { + uint16_t uuid_length; /*!< UUID length */ + uint8_t *uuid_p; /*!< UUID value */ + uint16_t perm; /*!< Attribute permission */ + uint16_t max_length; /*!< Maximum length of the element*/ + uint16_t length; /*!< Current length of the element*/ + uint8_t *value; /*!< Element value array*/ + } esp_attr_desc_t; + + +/** + * @brief attribute auto respose flag + */ +typedef struct +{ +#define ESP_GATT_RSP_BY_APP 0 +#define ESP_GATT_AUTO_RSP 1 + uint8_t auto_rsp; /*!< need the app response to the client if need_rsp set to 1*/ +} esp_attr_control_t; + + +/** + * @brief attribute type added to the gatt server database + */ +typedef struct +{ + esp_attr_control_t attr_control; /*!< The attribue control type*/ + esp_attr_desc_t att_desc; /*!< The attribue type*/ +} esp_gatts_attr_db_t; + + +/** + * @brief set the attribute value type + */ +typedef struct +{ + uint16_t attr_max_len; /*!< attribute max value length */ + uint16_t attr_len; /*!< attribute current value length */ + uint8_t *attr_value; /*!< the pointer to attribute value */ +} esp_attr_value_t; + + +/** + * @brief Gatt include service entry element + */ +typedef struct +{ + uint16_t start_hdl; /*!< Gatt start handle value of included service */ + uint16_t end_hdl; /*!< Gatt end handle value of included service */ + uint16_t uuid; /*!< Gatt attribute value UUID of included service */ +} esp_gatts_incl_svc_desc_t; /*!< Gatt include service entry element */ + +/** + * @brief Gatt include 128 bit service entry element + */ +typedef struct +{ + uint16_t start_hdl; /*!< Gatt start handle value of included 128 bit service */ + uint16_t end_hdl; /*!< Gatt end handle value of included 128 bit service */ +} esp_gatts_incl128_svc_desc_t; /*!< Gatt include 128 bit service entry element */ + + + + /// Gatt attribute value typedef struct { - uint8_t value[ESP_GATT_MAX_ATTR_LEN]; /*!< Gatt attribute value */ - uint16_t handle; /*!< Gatt attribute handle */ - uint16_t offset; /*!< Gatt attribute value offset */ - uint16_t len; /*!< Gatt attribute value length */ - uint8_t auth_req; /*!< Gatt authentication request */ + uint8_t value[ESP_GATT_MAX_ATTR_LEN]; /*!< Gatt attribute value */ + uint16_t handle; /*!< Gatt attribute handle */ + uint16_t offset; /*!< Gatt attribute value offset */ + uint16_t len; /*!< Gatt attribute value length */ + uint8_t auth_req; /*!< Gatt authentication request */ } esp_gatt_value_t; /// GATT remote read request response type typedef union { - esp_gatt_value_t attr_value; /*!< Gatt attribute structure */ - uint16_t handle; /*!< Gatt attribute handle */ + esp_gatt_value_t attr_value; /*!< Gatt attribute structure */ + uint16_t handle; /*!< Gatt attribute handle */ } esp_gatt_rsp_t; /** * @brief Gatt write type */ typedef enum { - ESP_GATT_WRITE_TYPE_NO_RSP = 1, /*!< Gatt write attribute need no response */ - ESP_GATT_WRITE_TYPE_RSP, /*!< Gatt write attribute need remote response */ + ESP_GATT_WRITE_TYPE_NO_RSP = 1, /*!< Gatt write attribute need no response */ + ESP_GATT_WRITE_TYPE_RSP, /*!< Gatt write attribute need remote response */ } esp_gatt_write_type_t; -typedef uint32_t esp_gatt_if_t; /*!< Gatt interface type, different application on GATT client use different gatt_if */ +#define ESP_GATT_IF_NONE 0xff /*!< If callback report gattc_if/gatts_if as this macro, means this event is not correspond to any app */ + +typedef uint8_t esp_gatt_if_t; /*!< Gatt interface type, different application on GATT client use different gatt_if */ #ifdef __cplusplus } diff --git a/components/bt/bluedroid/api/include/esp_gattc_api.h b/components/bt/bluedroid/api/include/esp_gattc_api.h index 4407975a0d..b52dabbdac 100644 --- a/components/bt/bluedroid/api/include/esp_gattc_api.h +++ b/components/bt/bluedroid/api/include/esp_gattc_api.h @@ -81,7 +81,6 @@ typedef union { */ struct gattc_reg_evt_param { esp_gatt_status_t status; /*!< Operation status */ - esp_gatt_if_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t app_id; /*!< Application id which input in register API */ } reg; /*!< Gatt client callback param of ESP_GATTC_REG_EVT */ @@ -91,7 +90,6 @@ typedef union { struct gattc_open_evt_param { esp_gatt_status_t status; /*!< Operation status */ uint16_t conn_id; /*!< Connection id */ - esp_gatt_if_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ uint16_t mtu; /*!< MTU size */ } open; /*!< Gatt client callback param of ESP_GATTC_OPEN_EVT */ @@ -102,7 +100,6 @@ typedef union { struct gattc_close_evt_param { esp_gatt_status_t status; /*!< Operation status */ uint16_t conn_id; /*!< Connection id */ - esp_gatt_if_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ esp_gatt_conn_reason_t reason; /*!< The reason of gatt connection close */ } close; /*!< Gatt client callback param of ESP_GATTC_CLOSE_EVT */ @@ -247,6 +244,14 @@ typedef union { } esp_ble_gattc_cb_param_t; /*!< GATT client callback parameter union type */ +/** + * @brief GATT Client callback function type + * @param event : Event type + * @param gatts_if : GATT client access interface, normally + * different gattc_if correspond to different profile + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_gattc_cb_t)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); /** * @brief This function is called to register application callbacks @@ -259,7 +264,7 @@ typedef union { * - other: failed * */ -esp_err_t esp_ble_gattc_register_callback(esp_profile_cb_t callback); +esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback); /** @@ -280,20 +285,20 @@ esp_err_t esp_ble_gattc_app_register(uint16_t app_id); * @brief This function is called to unregister an application * from GATTC module. * - * @param[in] gatt_if : app identifier. + * @param[in] gattc_if: Gatt client access interface. * * @return * - ESP_OK: success * - other: failed * */ -esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gatt_if); +esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gattc_if); /** * @brief Open a direct connection or add a background auto connection * - * @param[in] gatt_if: application identity. + * @param[in] gattc_if: Gatt client access interface. * @param[in] remote_bda: remote device bluetooth device address. * @param[in] is_direct: direct connection or background auto connection * @@ -302,12 +307,13 @@ esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gatt_if); * - other: failed * */ -esp_err_t esp_ble_gattc_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bool is_direct); +esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, bool is_direct); /** * @brief Close a connection to a GATT server. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID to be closed. * * @return @@ -315,13 +321,14 @@ esp_err_t esp_ble_gattc_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bo * - other: failed * */ -esp_err_t esp_ble_gattc_close(uint16_t conn_id); +esp_err_t esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id); /** * @brief Configure the MTU size in the GATT channel. This can be done * only once per connection. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID. * @param[in] mtu: desired MTU size to use. * @@ -330,7 +337,7 @@ esp_err_t esp_ble_gattc_close(uint16_t conn_id); * - other: failed * */ -esp_err_t esp_ble_gattc_config_mtu(uint16_t conn_id, uint16_t mtu); +esp_err_t esp_ble_gattc_config_mtu (esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t mtu); /** @@ -339,6 +346,7 @@ esp_err_t esp_ble_gattc_config_mtu(uint16_t conn_id, uint16_t mtu); * by a callback event, and followed by a service search complete * event. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID. * @param[in] filter_uuid: a UUID of the service application is interested in. * If Null, discover for all services. @@ -348,32 +356,32 @@ esp_err_t esp_ble_gattc_config_mtu(uint16_t conn_id, uint16_t mtu); * - other: failed * */ -esp_err_t esp_ble_gattc_search_service(uint16_t conn_id, esp_bt_uuid_t *filter_uuid); +esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *filter_uuid); /** * @brief This function is called to find the first characteristic of the * service on the given server. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID which identify the server. - * * @param[in] srvc_id: service ID - * * @param[in] start_char_id: the start characteristic ID - * * @return * - ESP_OK: success * - other: failed * */ -esp_err_t esp_ble_gattc_get_characteristic(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *start_char_id); - +esp_err_t esp_ble_gattc_get_characteristic(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *start_char_id); /** * @brief This function is called to find the descriptor of the * service on the given server. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID which identify the server. * @param[in] srvc_id: the service ID of which the characteristic is belonged to. * @param[in] char_id: Characteristic ID, if NULL find the first available @@ -385,8 +393,10 @@ esp_err_t esp_ble_gattc_get_characteristic(uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, +esp_err_t esp_ble_gattc_get_descriptor(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, esp_gatt_id_t *start_descr_id); @@ -394,6 +404,7 @@ esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, * @brief This function is called to find the first characteristic of the * service on the given server. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID which identify the server. * @param[in] srvc_id: the service ID of which the characteristic is belonged to. * @param[in] start_incl_srvc_id: the start include service id @@ -403,14 +414,17 @@ esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_get_included_service(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, esp_gatt_srvc_id_t *start_incl_srvc_id); +esp_err_t esp_ble_gattc_get_included_service(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_srvc_id_t *start_incl_srvc_id); /** * @brief This function is called to read a service's characteristics of - * the given characteriistic ID + * the given characteristic ID * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] srvc_id : service ID. * @param[in] char_id : characteristic ID to read. @@ -421,15 +435,17 @@ esp_err_t esp_ble_gattc_get_included_service(uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_read_char (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_auth_req_t auth_req); +esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_auth_req_t auth_req); /** * @brief This function is called to read a characteristics descriptor. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] srvc_id : service ID. * @param[in] char_id : characteristic ID to read. @@ -441,16 +457,18 @@ esp_err_t esp_ble_gattc_read_char (uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_read_char_descr (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_id_t *descr_id, - esp_gatt_auth_req_t auth_req); +esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_id_t *descr_id, + esp_gatt_auth_req_t auth_req); /** * @brief This function is called to write characteristic value. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] srvc_id : service ID. * @param[in] char_id : characteristic ID to write. @@ -464,7 +482,8 @@ esp_err_t esp_ble_gattc_read_char_descr (uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, +esp_err_t esp_ble_gattc_write_char( esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, uint16_t value_len, @@ -476,6 +495,7 @@ esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, /** * @brief This function is called to write characteristic descriptor value. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID * @param[in] srvc_id : service ID. * @param[in] char_id : characteristic ID. @@ -490,19 +510,21 @@ esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_write_char_descr (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_id_t *descr_id, - uint16_t value_len, - uint8_t *value, - esp_gatt_write_type_t write_type, - esp_gatt_auth_req_t auth_req); +esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_id_t *descr_id, + uint16_t value_len, + uint8_t *value, + esp_gatt_write_type_t write_type, + esp_gatt_auth_req_t auth_req); /** * @brief This function is called to prepare write a characteristic value. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] srvc_id : service ID. * @param[in] char_id : GATT characteristic ID of the service. @@ -516,7 +538,8 @@ esp_err_t esp_ble_gattc_write_char_descr (uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, +esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, uint16_t offset, @@ -524,9 +547,11 @@ esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, uint8_t *value, esp_gatt_auth_req_t auth_req); + /** * @brief This function is called to execute write a prepare write sequence. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] is_execute : execute or cancel. * @@ -535,13 +560,13 @@ esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_execute_write (uint16_t conn_id, bool is_execute); +esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, bool is_execute); /** * @brief This function is called to register for notification of a service. * - * @param[in] gatt_if : gatt interface id. + * @param[in] gattc_if: Gatt client access interface. * @param[in] server_bda : target GATT server. * @param[in] srvc_id : pointer to GATT service ID. * @param[in] char_id : pointer to GATT characteristic ID. @@ -551,16 +576,16 @@ esp_err_t esp_ble_gattc_execute_write (uint16_t conn_id, bool is_execute); * - other: failed * */ -esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, - esp_bd_addr_t server_bda, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id); +esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, + esp_bd_addr_t server_bda, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id); /** * @brief This function is called to de-register for notification of a service. * - * @param[in] gatt_if : gatt interface id. + * @param[in] gattc_if: Gatt client access interface. * @param[in] server_bda : target GATT server. * @param[in] srvc_id : pointer to GATT service ID. * @param[in] char_id : pointer to GATT characteristic ID. @@ -570,10 +595,10 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, * - other: failed * */ -esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gatt_if, - esp_bd_addr_t server_bda, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id); +esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, + esp_bd_addr_t server_bda, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id); #ifdef __cplusplus } diff --git a/components/bt/bluedroid/api/include/esp_gatts_api.h b/components/bt/bluedroid/api/include/esp_gatts_api.h index 30aa3ecf56..b18039ce79 100644 --- a/components/bt/bluedroid/api/include/esp_gatts_api.h +++ b/components/bt/bluedroid/api/include/esp_gatts_api.h @@ -25,29 +25,31 @@ extern "C" { /// GATT Server callback function events typedef enum { - ESP_GATTS_REG_EVT = 0, /*!< When register application id, the event comes */ - ESP_GATTS_READ_EVT = 1, /*!< When gatt client request read operation, the event comes */ - ESP_GATTS_WRITE_EVT = 2, /*!< When gatt client request write operation, the event comes */ - ESP_GATTS_EXEC_WRITE_EVT = 3, /*!< When gatt client request execute write, the event comes */ - ESP_GATTS_MTU_EVT = 4, /*!< When set mtu complete, the event comes */ - ESP_GATTS_CONF_EVT = 5, /*!< When receive confirm, the event comes */ - ESP_GATTS_UNREG_EVT = 6, /*!< When unregister application id, the event comes */ - ESP_GATTS_CREATE_EVT = 7, /*!< When create service complete, the event comes */ - ESP_GATTS_ADD_INCL_SRVC_EVT = 8, /*!< When add included service complete, the event comes */ - ESP_GATTS_ADD_CHAR_EVT = 9, /*!< When add characteristic complete, the event comes */ - ESP_GATTS_ADD_CHAR_DESCR_EVT = 10, /*!< When add descriptor complete, the event comes */ - ESP_GATTS_DELETE_EVT = 11, /*!< When delete service complete, the event comes */ - ESP_GATTS_START_EVT = 12, /*!< When start service complete, the event comes */ - ESP_GATTS_STOP_EVT = 13, /*!< When stop service complete, the event comes */ - ESP_GATTS_CONNECT_EVT = 14, /*!< When gatt client connect, the event comes */ - ESP_GATTS_DISCONNECT_EVT = 15, /*!< When gatt client disconnect, the event comes */ - ESP_GATTS_OPEN_EVT = 16, /*!< When connect to peer, the event comes */ - ESP_GATTS_CANCEL_OPEN_EVT = 17, /*!< When disconnect from peer, the event comes */ - ESP_GATTS_CLOSE_EVT = 18, /*!< When gatt server close, the event comes */ - ESP_GATTS_LISTEN_EVT = 19, /*!< When gatt listen to be connected the event comes */ - ESP_GATTS_CONGEST_EVT = 20, /*!< When congest happen, the event comes */ - /* following is extra event */ - ESP_GATTS_RESPONSE_EVT = 21, /*!< When gatt send response complete, the event comes */ + ESP_GATTS_REG_EVT = 0, /*!< When register application id, the event comes */ + ESP_GATTS_READ_EVT = 1, /*!< When gatt client request read operation, the event comes */ + ESP_GATTS_WRITE_EVT = 2, /*!< When gatt client request write operation, the event comes */ + ESP_GATTS_EXEC_WRITE_EVT = 3, /*!< When gatt client request execute write, the event comes */ + ESP_GATTS_MTU_EVT = 4, /*!< When set mtu complete, the event comes */ + ESP_GATTS_CONF_EVT = 5, /*!< When receive confirm, the event comes */ + ESP_GATTS_UNREG_EVT = 6, /*!< When unregister application id, the event comes */ + ESP_GATTS_CREATE_EVT = 7, /*!< When create service complete, the event comes */ + ESP_GATTS_ADD_INCL_SRVC_EVT = 8, /*!< When add included service complete, the event comes */ + ESP_GATTS_ADD_CHAR_EVT = 9, /*!< When add characteristic complete, the event comes */ + ESP_GATTS_ADD_CHAR_DESCR_EVT = 10, /*!< When add descriptor complete, the event comes */ + ESP_GATTS_DELETE_EVT = 11, /*!< When delete service complete, the event comes */ + ESP_GATTS_START_EVT = 12, /*!< When start service complete, the event comes */ + ESP_GATTS_STOP_EVT = 13, /*!< When stop service complete, the event comes */ + ESP_GATTS_CONNECT_EVT = 14, /*!< When gatt client connect, the event comes */ + ESP_GATTS_DISCONNECT_EVT = 15, /*!< When gatt client disconnect, the event comes */ + ESP_GATTS_OPEN_EVT = 16, /*!< When connect to peer, the event comes */ + ESP_GATTS_CANCEL_OPEN_EVT = 17, /*!< When disconnect from peer, the event comes */ + ESP_GATTS_CLOSE_EVT = 18, /*!< When gatt server close, the event comes */ + ESP_GATTS_LISTEN_EVT = 19, /*!< When gatt listen to be connected the event comes */ + ESP_GATTS_CONGEST_EVT = 20, /*!< When congest happen, the event comes */ + /* following is extra event */ + ESP_GATTS_RESPONSE_EVT = 21, /*!< When gatt send response complete, the event comes */ + ESP_GATTS_CREAT_ATTR_TAB_EVT = 22, + ESP_GATTS_SET_ATTR_VAL_EVT = 23, } esp_gatts_cb_event_t; /** @@ -58,65 +60,66 @@ typedef union { * @brief ESP_GATTS_REG_EVT */ struct gatts_reg_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - uint16_t app_id; /*!< Application id which input in register API */ - } reg; /*!< Gatt server callback param of ESP_GATTS_REG_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t app_id; /*!< Application id which input in register API */ + } reg; /*!< Gatt server callback param of ESP_GATTS_REG_EVT */ /** * @brief ESP_GATTS_READ_EVT */ struct gatts_read_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint32_t trans_id; /*!< Transfer id */ - esp_bd_addr_t bda; /*!< The bluetooth device address which been read */ - uint16_t handle; /*!< The attribute handle */ - uint16_t offset; /*!< Offset of the value, if the value is too long */ - bool is_long; /*!< The value is too long or not */ - } read; /*!< Gatt server callback param of ESP_GATTS_READ_EVT */ + uint16_t conn_id; /*!< Connection id */ + uint32_t trans_id; /*!< Transfer id */ + esp_bd_addr_t bda; /*!< The bluetooth device address which been read */ + uint16_t handle; /*!< The attribute handle */ + uint16_t offset; /*!< Offset of the value, if the value is too long */ + bool is_long; /*!< The value is too long or not */ + bool need_rsp; /*!< The read operation need to do response */ + } read; /*!< Gatt server callback param of ESP_GATTS_READ_EVT */ + /** * @brief ESP_GATTS_WRITE_EVT */ struct gatts_write_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint32_t trans_id; /*!< Transfer id */ - esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ - uint16_t handle; /*!< The attribute handle */ - uint16_t offset; /*!< Offset of the value, if the value is too long */ - bool need_rsp; /*!< The write operation need to do response */ - bool is_prep; /*!< This write operation is prepare write */ - uint16_t len; /*!< The write attribute value length */ - uint8_t *value; /*!< The write attribute value */ - } write; /*!< Gatt server callback param of ESP_GATTS_WRITE_EVT */ + uint16_t conn_id; /*!< Connection id */ + uint32_t trans_id; /*!< Transfer id */ + esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ + uint16_t handle; /*!< The attribute handle */ + uint16_t offset; /*!< Offset of the value, if the value is too long */ + bool need_rsp; /*!< The write operation need to do response */ + bool is_prep; /*!< This write operation is prepare write */ + uint16_t len; /*!< The write attribute value length */ + uint8_t *value; /*!< The write attribute value */ + } write; /*!< Gatt server callback param of ESP_GATTS_WRITE_EVT */ /** * @brief ESP_GATTS_EXEC_WRITE_EVT */ struct gatts_exec_write_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint32_t trans_id; /*!< Transfer id */ - esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ -#define ESP_GATT_PREP_WRITE_CANCEL 0x00 -#define ESP_GATT_PREP_WRITE_EXEC 0x01 - uint8_t exec_write_flag; /*!< Execute write flag */ - } exec_write; /*!< Gatt server callback param of ESP_GATTS_EXEC_WRITE_EVT */ + uint16_t conn_id; /*!< Connection id */ + uint32_t trans_id; /*!< Transfer id */ + esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ +#define ESP_GATT_PREP_WRITE_CANCEL 0x00 /*!< Prepare write flag to indicate cancel prepare write */ +#define ESP_GATT_PREP_WRITE_EXEC 0x01 /*!< Prepare write flag to indicate execute prepare write */ + uint8_t exec_write_flag; /*!< Execute write flag */ + } exec_write; /*!< Gatt server callback param of ESP_GATTS_EXEC_WRITE_EVT */ /** * @brief ESP_GATTS_MTU_EVT */ struct gatts_mtu_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint16_t mtu; /*!< MTU size */ - } mtu; /*!< Gatt server callback param of ESP_GATTS_MTU_EVT */ + uint16_t conn_id; /*!< Connection id */ + uint16_t mtu; /*!< MTU size */ + } mtu; /*!< Gatt server callback param of ESP_GATTS_MTU_EVT */ /** * @brief ESP_GATTS_CONF_EVT */ struct gatts_conf_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t conn_id; /*!< Connection id */ - } conf; /*!< Gatt server callback param of ESP_GATTS_CONF_EVT (confirm) */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + } conf; /*!< Gatt server callback param of ESP_GATTS_CONF_EVT (confirm) */ /** * @brief ESP_GATTS_UNREG_EVT @@ -126,90 +129,81 @@ typedef union { * @brief ESP_GATTS_CREATE_EVT */ struct gatts_create_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - uint16_t service_handle; /*!< Service attribute handle */ - esp_gatt_srvc_id_t service_id; /*!< Service id, include service uuid and other information */ - } create; /*!< Gatt server callback param of ESP_GATTS_CREATE_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + esp_gatt_srvc_id_t service_id; /*!< Service id, include service uuid and other information */ + } create; /*!< Gatt server callback param of ESP_GATTS_CREATE_EVT */ /** * @brief ESP_GATTS_ADD_INCL_SRVC_EVT */ struct gatts_add_incl_srvc_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - uint16_t attr_handle; /*!< Included service attribute handle */ - uint16_t service_handle; /*!< Service attribute handle */ - } add_incl_srvc; /*!< Gatt server callback param of ESP_GATTS_ADD_INCL_SRVC_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t attr_handle; /*!< Included service attribute handle */ + uint16_t service_handle; /*!< Service attribute handle */ + } add_incl_srvc; /*!< Gatt server callback param of ESP_GATTS_ADD_INCL_SRVC_EVT */ /** * @brief ESP_GATTS_ADD_CHAR_EVT */ struct gatts_add_char_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - uint16_t attr_handle; /*!< Characteristic attribute handle */ - uint16_t service_handle; /*!< Service attribute handle */ - esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ - } add_char; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t attr_handle; /*!< Characteristic attribute handle */ + uint16_t service_handle; /*!< Service attribute handle */ + esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ + } add_char; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_EVT */ /** * @brief ESP_GATTS_ADD_CHAR_DESCR_EVT */ struct gatts_add_char_descr_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - uint16_t attr_handle; /*!< Descriptor attribute handle */ - uint16_t service_handle; /*!< Service attribute handle */ - esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ - } add_char_descr; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_DESCR_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t attr_handle; /*!< Descriptor attribute handle */ + uint16_t service_handle; /*!< Service attribute handle */ + esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ + } add_char_descr; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_DESCR_EVT */ /** * @brief ESP_GATTS_DELETE_EVT */ struct gatts_delete_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - uint16_t service_handle; /*!< Service attribute handle */ - } del; /*!< Gatt server callback param of ESP_GATTS_DELETE_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + } del; /*!< Gatt server callback param of ESP_GATTS_DELETE_EVT */ /** * @brief ESP_GATTS_START_EVT */ struct gatts_start_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - uint16_t service_handle; /*!< Service attribute handle */ - } start; /*!< Gatt server callback param of ESP_GATTS_START_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + } start; /*!< Gatt server callback param of ESP_GATTS_START_EVT */ /** * @brief ESP_GATTS_STOP_EVT */ struct gatts_stop_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - uint16_t service_handle; /*!< Service attribute handle */ - } stop; /*!< Gatt server callback param of ESP_GATTS_STOP_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + } stop; /*!< Gatt server callback param of ESP_GATTS_STOP_EVT */ /** * @brief ESP_GATTS_CONNECT_EVT */ struct gatts_connect_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ - bool is_connected; /*!< Indicate it is connected or not */ - } connect; /*!< Gatt server callback param of ESP_GATTS_CONNECT_EVT */ + uint16_t conn_id; /*!< Connection id */ + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + bool is_connected; /*!< Indicate it is connected or not */ + } connect; /*!< Gatt server callback param of ESP_GATTS_CONNECT_EVT */ /** * @brief ESP_GATTS_DISCONNECT_EVT */ struct gatts_disconnect_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ - esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ - bool is_connected; /*!< Indicate it is connected or not */ - } disconnect; /*!< Gatt server callback param of ESP_GATTS_DISCONNECT_EVT */ + uint16_t conn_id; /*!< Connection id */ + esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + bool is_connected; /*!< Indicate it is connected or not */ + } disconnect; /*!< Gatt server callback param of ESP_GATTS_DISCONNECT_EVT */ /** * @brief ESP_GATTS_OPEN_EVT @@ -227,19 +221,49 @@ typedef union { * @brief ESP_GATTS_CONGEST_EVT */ struct gatts_congest_evt_param { - uint16_t conn_id; /*!< Connection id */ - bool congested; /*!< Congested or not */ - } congest; /*!< Gatt server callback param of ESP_GATTS_CONGEST_EVT */ + uint16_t conn_id; /*!< Connection id */ + bool congested; /*!< Congested or not */ + } congest; /*!< Gatt server callback param of ESP_GATTS_CONGEST_EVT */ /** * @brief ESP_GATTS_RESPONSE_EVT */ struct gatts_rsp_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t handle; /*!< Attribute handle which send response */ - } rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t handle; /*!< Attribute handle which send response */ + } rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */ + + /** + * @brief ESP_GATTS_CREAT_ATTR_TAB_EVT + */ + struct gatts_add_attr_tab_evt_param{ + esp_gatt_status_t status; /*!< Operation status */ + esp_bt_uuid_t svc_uuid; /*!< Service uuid type */ + uint16_t num_handle; /*!< The number of the attribute handle to be added to the gatts database */ + uint16_t *handles; /*!< The number to the handles */ + } add_attr_tab; /*!< Gatt server callback param of ESP_GATTS_CREAT_ATTR_TAB_EVT */ + + + /** + * @brief ESP_GATTS_SET_ATTR_VAL_EVT + */ + struct gatts_set_attr_val_evt_param{ + uint16_t srvc_handle; /*!< The service handle */ + uint16_t attr_handle; /*!< The attribute handle */ + esp_gatt_status_t status; /*!< Operation status*/ + } set_attr_val; /*!< Gatt server callback param of ESP_GATTS_SET_ATTR_VAL_EVT */ + } esp_ble_gatts_cb_param_t; +/** + * @brief GATT Server callback function type + * @param event : Event type + * @param gatts_if : GATT server access interface, normally + * different gatts_if correspond to different profile + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_gatts_cb_t)(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + /** * @brief This function is called to register application callbacks * with BTA GATTS module. @@ -249,9 +273,7 @@ typedef union { * - other : failed * */ -esp_err_t esp_ble_gatts_register_callback(esp_profile_cb_t callback); - - +esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback); /** * @brief This function is called to register application identifier @@ -268,14 +290,13 @@ esp_err_t esp_ble_gatts_app_register(uint16_t app_id); /** * @brief unregister with GATT Server. * - * @param[in] gatt_if: gatt interface id. - * + * @param[in] gatts_if: GATT server access interface * @return * - ESP_OK : success * - other : failed * */ -esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatt_if); +esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if); /** @@ -285,7 +306,7 @@ esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatt_if); * the callback function needs to be used when adding included * service and characteristics/descriptors into the service. * - * @param[in] gatt_if: gatt interface ID + * @param[in] gatts_if: GATT server access interface * @param[in] service_id: service ID. * @param[in] num_handle: number of handle requested for this service. * @@ -294,11 +315,26 @@ esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatt_if); * - other : failed * */ -esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatt_if, +esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, esp_gatt_srvc_id_t *service_id, uint16_t num_handle); - +/** + * @brief Create a service attribute tab. + * @param[in] gatts_attr_db: the pointer to the service attr tab + * @param[in] gatts_if: GATT server access interface + * @param[in] max_nb_attr: the number of attribute to be added to the service database. + * @param[in] srvc_inst_id: the instance id of the service + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t *gatts_attr_db, + esp_gatt_if_t gatts_if, + uint8_t max_nb_attr, + uint8_t srvc_inst_id); /** * @brief This function is called to add an included service. After included * service is included, a callback event BTA_GATTS_ADD_INCL_SRVC_EVT @@ -325,6 +361,8 @@ esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t i * @param[in] char_uuid : Characteristic UUID. * @param[in] perm : Characteristic value declaration attribute permission. * @param[in] property : Characteristic Properties + * @param[in] char_val : Characteristic value + * @param[in] control : attribute response control byte * * @return * - ESP_OK : success @@ -332,7 +370,8 @@ esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t i * */ esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_uuid, - esp_gatt_perm_t perm, esp_gatt_char_prop_t property); + esp_gatt_perm_t perm, esp_gatt_char_prop_t property, esp_attr_value_t *char_val, + esp_attr_control_t *control); /** @@ -344,15 +383,17 @@ esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_ * be added. * @param[in] perm: descriptor access permission. * @param[in] descr_uuid: descriptor UUID. - * + * @param[in] char_descr_val : Characteristic descriptor value + * @param[in] control : attribute response control byte * @return * - ESP_OK : success * - other : failed * */ esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, - esp_bt_uuid_t *descr_uuid, - esp_gatt_perm_t perm); + esp_bt_uuid_t *descr_uuid, + esp_gatt_perm_t perm, esp_attr_value_t *char_descr_val, + esp_attr_control_t *control); @@ -400,8 +441,10 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle); /** - * @brief This function is called to read a characteristics descriptor. + * @brief Send indicate or notify to GATT client. + * Set param need_confirm as false will send notification, otherwise indication. * + * @param[in] gatts_if: GATT server access interface * @param[in] conn_id - connection id to indicate. * @param[in] attr_handle - attribute handle to indicate. * @param[in] value_len - indicate value length. @@ -413,13 +456,14 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle); * - other : failed * */ -esp_err_t esp_ble_gatts_send_indicate(uint16_t conn_id, uint16_t attr_handle, +esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t attr_handle, uint16_t value_len, uint8_t *value, bool need_confirm); /** * @brief This function is called to send a response to a request. * + * @param[in] gatts_if: GATT server access interface * @param[in] conn_id - connection identifier. * @param[in] trans_id - transfer id * @param[in] status - response status @@ -430,14 +474,43 @@ esp_err_t esp_ble_gatts_send_indicate(uint16_t conn_id, uint16_t attr_handle, * - other : failed * */ -esp_err_t esp_ble_gatts_send_response(uint16_t conn_id, uint32_t trans_id, +esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id, esp_gatt_status_t status, esp_gatt_rsp_t *rsp); +/** + * @brief This function is called to set the attribute value by the application + * + * @param[in] attr_handle: the attribute handle which to be set + * @param[in] length: the value length + * @param[in] value: the pointer to the attribute value + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_set_attr_value(uint16_t attr_handle, uint16_t length, const uint8_t *value); + +/** + * @brief Retrieve attribute value + * + * @param[in] attr_handle: Attribute handle. + * @param[out] length: pointer to the attribute value length + * @param[out] value: Pointer to attribute value payload, the value cannot be modified by user + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value); + + /** * @brief Open a direct open connection or add a background auto connection * - * @param[in] gatt_if: application ID. + * @param[in] gatts_if: GATT server access interface * @param[in] remote_bda: remote device bluetooth device address. * @param[in] is_direct: direct connection or background auto connection * @@ -446,11 +519,12 @@ esp_err_t esp_ble_gatts_send_response(uint16_t conn_id, uint32_t trans_id, * - other : failed * */ -esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bool is_direct); +esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, bool is_direct); /** * @brief Close a connection a remote device. * + * @param[in] gatts_if: GATT server access interface * @param[in] conn_id: connection ID to be closed. * * @return @@ -458,7 +532,7 @@ esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bo * - other : failed * */ -esp_err_t esp_ble_gatts_close(uint16_t conn_id); +esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id); #ifdef __cplusplus } diff --git a/components/bt/bluedroid/bta/dm/bta_dm_api.c b/components/bt/bluedroid/bta/dm/bta_dm_api.c index 1709880555..914bbfabc2 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_api.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_api.c @@ -1014,8 +1014,8 @@ void BTA_DmSetBleAdvParamsAll (UINT16 adv_int_min, UINT16 adv_int_max, #if BLE_INCLUDED == TRUE tBTA_DM_API_BLE_ADV_PARAMS_ALL *p_msg; - APPL_TRACE_ERROR ("BTA_DmSetBleAdvParamsAll: %d, %d\n", adv_int_min, adv_int_max); - APPL_TRACE_ERROR ("adv_type = %d, addr_type_own = %d, chnl_map = %d, adv_fil_pol = %d\n", + APPL_TRACE_API ("BTA_DmSetBleAdvParamsAll: %d, %d\n", adv_int_min, adv_int_max); + APPL_TRACE_API ("adv_type = %d, addr_type_own = %d, chnl_map = %d, adv_fil_pol = %d\n", adv_type, addr_type_own, chnl_map, adv_fil_pol); if ((p_msg = (tBTA_DM_API_BLE_ADV_PARAMS_ALL *) GKI_getbuf(sizeof(tBTA_DM_API_BLE_ADV_PARAMS_ALL) + sizeof(tBLE_BD_ADDR))) != NULL) { diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index aa00bf287d..83d5e4d7d7 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -34,7 +34,6 @@ #include "bta_gatts_int.h" #include "bta_gatts_co.h" #include "btm_ble_api.h" -// #include "btif/include/btif_debug_conn.h" #include static void bta_gatts_nv_save_cback(BOOLEAN is_saved, tGATTS_HNDL_RANGE *p_hndl_range); @@ -233,8 +232,6 @@ void bta_gatts_register(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) if (p_msg->api_reg.p_cback) { (*p_msg->api_reg.p_cback)(BTA_GATTS_REG_EVT, &cb_data); } - - LOG_ERROR("status=%x\n", status); } @@ -406,10 +403,22 @@ void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) UINT16 attr_id = 0; tBTA_GATTS cb_data; + tGATT_ATTR_VAL *p_attr_val = NULL; + tGATTS_ATTR_CONTROL *p_control = NULL; + + if(p_msg->api_add_char.attr_val.attr_max_len != 0){ + p_attr_val = &p_msg->api_add_char.attr_val; + } + + if(p_msg->api_add_char.control.auto_rsp != 0){ + p_control = &p_msg->api_add_char.control; + } + + attr_id = GATTS_AddCharacteristic(p_msg->api_add_char.hdr.layer_specific, &p_msg->api_add_char.char_uuid, p_msg->api_add_char.perm, - p_msg->api_add_char.property); + p_msg->api_add_char.property, p_attr_val, p_control); cb_data.add_result.server_if = p_rcb->gatt_if; cb_data.add_result.service_id = p_msg->api_add_incl_srvc.hdr.layer_specific; cb_data.add_result.attr_id = attr_id; @@ -422,11 +431,15 @@ void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) } else { cb_data.add_result.status = BTA_GATT_ERROR; } + if((p_attr_val != NULL) && (p_attr_val->attr_val != NULL)){ + GKI_freebuf(p_attr_val->attr_val); + } if (p_rcb->p_cback) { (*p_rcb->p_cback)(BTA_GATTS_ADD_CHAR_EVT, &cb_data); } } + /******************************************************************************* ** ** Function bta_gatts_add_char_descr @@ -441,10 +454,20 @@ void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; UINT16 attr_id = 0; tBTA_GATTS cb_data; + tGATT_ATTR_VAL *p_attr_val = NULL; + tGATTS_ATTR_CONTROL *p_control = NULL; + if (p_msg->api_add_char_descr.attr_val.attr_max_len != 0) { + p_attr_val = &p_msg->api_add_char_descr.attr_val; + } + + if (p_msg->api_add_char_descr.control.auto_rsp != 0) { + p_control = &p_msg->api_add_char_descr.control; + } attr_id = GATTS_AddCharDescriptor(p_msg->api_add_char_descr.hdr.layer_specific, p_msg->api_add_char_descr.perm, - &p_msg->api_add_char_descr.descr_uuid); + &p_msg->api_add_char_descr.descr_uuid, p_attr_val, + p_control); cb_data.add_result.server_if = p_rcb->gatt_if; cb_data.add_result.service_id = p_msg->api_add_incl_srvc.hdr.layer_specific; @@ -458,12 +481,50 @@ void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ } else { cb_data.add_result.status = BTA_GATT_ERROR; } + if((p_attr_val != NULL) && (p_attr_val->attr_val != NULL)){ + GKI_freebuf(p_attr_val->attr_val); + } if (p_rcb->p_cback) { (*p_rcb->p_cback)(BTA_GATTS_ADD_CHAR_DESCR_EVT, &cb_data); } } + +/******************************************************************************* +** +** Function bta_gatts_add_char_descr +** +** Description action function to add characteristic descriptor. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; + UINT16 attr_id = 0; + tBTA_GATTS cb_data; + tBTA_GATT_STATUS gatts_status; + gatts_status = GATTS_SetAttributeValue(p_msg->api_add_char_descr.hdr.layer_specific, + p_msg->api_set_val.length, + p_msg->api_set_val.value); + + cb_data.attr_val.server_if = p_rcb->gatt_if; + cb_data.attr_val.service_id = p_msg->api_set_val.hdr.layer_specific; + cb_data.attr_val.attr_id = attr_id; + cb_data.attr_val.status = gatts_status; + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_SET_ATTR_VAL_EVT, &cb_data); + } +} + +void bta_gatts_get_attr_value(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + GATTS_GetAttributeValue(attr_handle, length, value); +} + /******************************************************************************* ** ** Function bta_gatts_delete_service diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_api.c b/components/bt/bluedroid/bta/gatt/bta_gatts_api.c index d54935120c..94f1d407e8 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_api.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_api.c @@ -215,10 +215,14 @@ void BTA_GATTS_AddIncludeService(UINT16 service_id, UINT16 included_service_id) ** *******************************************************************************/ void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, - tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property) + tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control) { tBTA_GATTS_API_ADD_CHAR *p_buf; - + UINT16 len = 0; + if(attr_val != NULL){ + len = attr_val->attr_len; + } if ((p_buf = (tBTA_GATTS_API_ADD_CHAR *) GKI_getbuf(sizeof(tBTA_GATTS_API_ADD_CHAR))) != NULL) { memset(p_buf, 0, sizeof(tBTA_GATTS_API_ADD_CHAR)); @@ -226,6 +230,19 @@ void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, p_buf->hdr.layer_specific = service_id; p_buf->perm = perm; p_buf->property = property; + if(control !=NULL){ + p_buf->control.auto_rsp = control->auto_rsp; + } + if(attr_val != NULL){ + APPL_TRACE_DEBUG("!!!!!!attr_val->attr_len = %x\n",attr_val->attr_len); + APPL_TRACE_DEBUG("!!!!!!!attr_val->attr_max_len = %x\n",attr_val->attr_max_len); + p_buf->attr_val.attr_len = attr_val->attr_len; + p_buf->attr_val.attr_max_len = attr_val->attr_max_len; + p_buf->attr_val.attr_val = (uint8_t *)GKI_getbuf(len); + if(p_buf->attr_val.attr_val != NULL){ + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + } if (p_char_uuid) { memcpy(&p_buf->char_uuid, p_char_uuid, sizeof(tBT_UUID)); @@ -253,22 +270,43 @@ void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, *******************************************************************************/ void BTA_GATTS_AddCharDescriptor (UINT16 service_id, tBTA_GATT_PERM perm, - tBT_UUID *p_descr_uuid) + tBT_UUID *p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control) { tBTA_GATTS_API_ADD_DESCR *p_buf; - UINT16 len = sizeof(tBTA_GATTS_API_ADD_DESCR); + UINT16 value_len = 0; - - if ((p_buf = (tBTA_GATTS_API_ADD_DESCR *) GKI_getbuf(len)) != NULL) { - memset(p_buf, 0, len); + if ((p_buf = (tBTA_GATTS_API_ADD_DESCR *) GKI_getbuf(sizeof(tBTA_GATTS_API_ADD_DESCR))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTS_API_ADD_DESCR)); p_buf->hdr.event = BTA_GATTS_API_ADD_DESCR_EVT; p_buf->hdr.layer_specific = service_id; p_buf->perm = perm; + if(control != NULL){ + p_buf->control.auto_rsp = control->auto_rsp; + } + if (p_descr_uuid) { memcpy(&p_buf->descr_uuid, p_descr_uuid, sizeof(tBT_UUID)); } + + if(attr_val != NULL){ + p_buf->attr_val.attr_len = attr_val->attr_len; + p_buf->attr_val.attr_max_len = attr_val->attr_max_len; + value_len = attr_val->attr_len; + if (value_len != 0){ + p_buf->attr_val.attr_val = (uint8_t*)GKI_getbuf(value_len); + if(p_buf->attr_val.attr_val != NULL){ + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, value_len); + } + else{ + APPL_TRACE_ERROR("Allocate fail for %s\n", __func__); + + } + } + } + bta_sys_sendmsg(p_buf); } return; @@ -276,14 +314,14 @@ void BTA_GATTS_AddCharDescriptor (UINT16 service_id, } /******************************************************************************* -** -** Function BTA_GATTS_DeleteService -** -** Description This function is called to delete a service. When this is done, -** a callback event BTA_GATTS_DELETE_EVT is report with the status. -** -** Parameters service_id: service_id to be deleted. -** + ** + ** Function BTA_GATTS_DeleteService + ** + ** Description This function is called to delete a service. When this is done, + ** a callback event BTA_GATTS_DELETE_EVT is report with the status. + ** + ** Parameters service_id: service_id to be deleted. + ** ** Returns returns none. ** *******************************************************************************/ @@ -433,6 +471,29 @@ void BTA_GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, } +void BTA_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value) +{ + tBTA_GATTS_API_SET_ATTR_VAL *p_buf; + if((p_buf = (tBTA_GATTS_API_SET_ATTR_VAL *)GKI_getbuf( + sizeof(tBTA_GATTS_API_SET_ATTR_VAL))) != NULL){ + p_buf->hdr.event = BTA_GATTS_API_SET_ATTR_VAL_EVT; + p_buf->hdr.layer_specific = attr_handle; + p_buf->length = length; + if(value != NULL){ + if((p_buf->value = (UINT8 *)GKI_getbuf(length)) != NULL){ + memcpy(p_buf->value, value, length); + } + } + + bta_sys_sendmsg(p_buf); + } + +} + +void BTA_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + bta_gatts_get_attr_value(attr_handle, length, value); +} /******************************************************************************* ** diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_main.c b/components/bt/bluedroid/bta/gatt/bta_gatts_main.c index d59115d42d..4306709c8c 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_main.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_main.c @@ -104,29 +104,30 @@ BOOLEAN bta_gatts_hdl_event(BT_HDR *p_msg) case BTA_GATTS_API_RSP_EVT: bta_gatts_send_rsp(p_cb, (tBTA_GATTS_DATA *) p_msg); break; - + case BTA_GATTS_API_SET_ATTR_VAL_EVT:{ + UINT16 attr_id = ((tBTA_GATTS_DATA *) p_msg)->api_set_val.hdr.layer_specific; + p_srvc_cb = bta_gatts_find_srvc_cb_by_attr_id(p_cb, attr_id); + bta_gatts_set_attr_value(p_srvc_cb, (tBTA_GATTS_DATA *) p_msg); + break; + } case BTA_GATTS_API_LISTEN_EVT: bta_gatts_listen(p_cb, (tBTA_GATTS_DATA *) p_msg); break; - - case BTA_GATTS_API_ADD_INCL_SRVC_EVT: case BTA_GATTS_API_ADD_CHAR_EVT: case BTA_GATTS_API_ADD_DESCR_EVT: case BTA_GATTS_API_DEL_SRVC_EVT: case BTA_GATTS_API_START_SRVC_EVT: case BTA_GATTS_API_STOP_SRVC_EVT: - p_srvc_cb = bta_gatts_find_srvc_cb_by_srvc_id(p_cb, ((tBTA_GATTS_DATA *)p_msg)->api_add_incl_srvc.hdr.layer_specific); if (p_srvc_cb != NULL) { bta_gatts_srvc_build_act[p_msg->event - BTA_GATTS_API_ADD_INCL_SRVC_EVT](p_srvc_cb, (tBTA_GATTS_DATA *) p_msg); } else { - APPL_TRACE_ERROR("service not created"); + APPL_TRACE_ERROR("service not created\n"); } break; - default: break; } diff --git a/components/bt/bluedroid/bta/include/bta_gatt_api.h b/components/bt/bluedroid/bta/include/bta_gatt_api.h index d10ca46aee..f4c0e06f07 100644 --- a/components/bt/bluedroid/bta/include/bta_gatt_api.h +++ b/components/bt/bluedroid/bta/include/bta_gatt_api.h @@ -109,41 +109,41 @@ typedef UINT8 tBTA_GATT_STATUS; /* Client callback function events */ -#define BTA_GATTC_REG_EVT 0 /* GATT client is registered. */ -#define BTA_GATTC_DEREG_EVT 1 /* GATT client deregistered event */ -#define BTA_GATTC_OPEN_EVT 2 /* GATTC open request status event */ -#define BTA_GATTC_READ_CHAR_EVT 3 /* GATT read characteristic event */ -#define BTA_GATTC_WRITE_CHAR_EVT 4 /* GATT write characteristic or char descriptor event */ -#define BTA_GATTC_CLOSE_EVT 5 /* GATTC close request status event */ -#define BTA_GATTC_SEARCH_CMPL_EVT 6 /* GATT discovery complete event */ -#define BTA_GATTC_SEARCH_RES_EVT 7 /* GATT discovery result event */ -#define BTA_GATTC_READ_DESCR_EVT 8 /* GATT read characterisitc descriptor event */ -#define BTA_GATTC_WRITE_DESCR_EVT 9 /* GATT write characteristic descriptor event */ -#define BTA_GATTC_NOTIF_EVT 10 /* GATT attribute notification event */ -#define BTA_GATTC_PREP_WRITE_EVT 11 /* GATT prepare write event */ -#define BTA_GATTC_EXEC_EVT 12 /* execute write complete event */ -#define BTA_GATTC_ACL_EVT 13 /* ACL up event */ -#define BTA_GATTC_CANCEL_OPEN_EVT 14 /* cancel open event */ -#define BTA_GATTC_SRVC_CHG_EVT 15 /* service change event */ -#define BTA_GATTC_LISTEN_EVT 16 /* listen event */ -#define BTA_GATTC_ENC_CMPL_CB_EVT 17 /* encryption complete callback event */ -#define BTA_GATTC_CFG_MTU_EVT 18 /* configure MTU complete event */ -#define BTA_GATTC_ADV_DATA_EVT 19 /* ADV data event */ -#define BTA_GATTC_MULT_ADV_ENB_EVT 20 /* Enable Multi ADV event */ -#define BTA_GATTC_MULT_ADV_UPD_EVT 21 /* Update parameter event */ -#define BTA_GATTC_MULT_ADV_DATA_EVT 22 /* Multi ADV data event */ -#define BTA_GATTC_MULT_ADV_DIS_EVT 23 /* Disable Multi ADV event */ -#define BTA_GATTC_CONGEST_EVT 24 /* Congestion event */ -#define BTA_GATTC_BTH_SCAN_ENB_EVT 25 /* Enable batch scan event */ -#define BTA_GATTC_BTH_SCAN_CFG_EVT 26 /* Config storage event */ -#define BTA_GATTC_BTH_SCAN_RD_EVT 27 /* Batch scan reports read event */ -#define BTA_GATTC_BTH_SCAN_THR_EVT 28 /* Batch scan threshold event */ -#define BTA_GATTC_BTH_SCAN_PARAM_EVT 29 /* Batch scan param event */ -#define BTA_GATTC_BTH_SCAN_DIS_EVT 30 /* Disable batch scan event */ -#define BTA_GATTC_SCAN_FLT_CFG_EVT 31 /* Scan filter config event */ -#define BTA_GATTC_SCAN_FLT_PARAM_EVT 32 /* Param filter event */ -#define BTA_GATTC_SCAN_FLT_STATUS_EVT 33 /* Filter status event */ -#define BTA_GATTC_ADV_VSC_EVT 34 /* ADV VSC event */ +#define BTA_GATTC_REG_EVT 0 /* GATT client is registered. */ +#define BTA_GATTC_DEREG_EVT 1 /* GATT client deregistered event */ +#define BTA_GATTC_OPEN_EVT 2 /* GATTC open request status event */ +#define BTA_GATTC_READ_CHAR_EVT 3 /* GATT read characteristic event */ +#define BTA_GATTC_WRITE_CHAR_EVT 4 /* GATT write characteristic or char descriptor event */ +#define BTA_GATTC_CLOSE_EVT 5 /* GATTC close request status event */ +#define BTA_GATTC_SEARCH_CMPL_EVT 6 /* GATT discovery complete event */ +#define BTA_GATTC_SEARCH_RES_EVT 7 /* GATT discovery result event */ +#define BTA_GATTC_READ_DESCR_EVT 8 /* GATT read characterisitc descriptor event */ +#define BTA_GATTC_WRITE_DESCR_EVT 9 /* GATT write characteristic descriptor event */ +#define BTA_GATTC_NOTIF_EVT 10 /* GATT attribute notification event */ +#define BTA_GATTC_PREP_WRITE_EVT 11 /* GATT prepare write event */ +#define BTA_GATTC_EXEC_EVT 12 /* execute write complete event */ +#define BTA_GATTC_ACL_EVT 13 /* ACL up event */ +#define BTA_GATTC_CANCEL_OPEN_EVT 14 /* cancel open event */ +#define BTA_GATTC_SRVC_CHG_EVT 15 /* service change event */ +#define BTA_GATTC_LISTEN_EVT 16 /* listen event */ +#define BTA_GATTC_ENC_CMPL_CB_EVT 17 /* encryption complete callback event */ +#define BTA_GATTC_CFG_MTU_EVT 18 /* configure MTU complete event */ +#define BTA_GATTC_ADV_DATA_EVT 19 /* ADV data event */ +#define BTA_GATTC_MULT_ADV_ENB_EVT 20 /* Enable Multi ADV event */ +#define BTA_GATTC_MULT_ADV_UPD_EVT 21 /* Update parameter event */ +#define BTA_GATTC_MULT_ADV_DATA_EVT 22 /* Multi ADV data event */ +#define BTA_GATTC_MULT_ADV_DIS_EVT 23 /* Disable Multi ADV event */ +#define BTA_GATTC_CONGEST_EVT 24 /* Congestion event */ +#define BTA_GATTC_BTH_SCAN_ENB_EVT 25 /* Enable batch scan event */ +#define BTA_GATTC_BTH_SCAN_CFG_EVT 26 /* Config storage event */ +#define BTA_GATTC_BTH_SCAN_RD_EVT 27 /* Batch scan reports read event */ +#define BTA_GATTC_BTH_SCAN_THR_EVT 28 /* Batch scan threshold event */ +#define BTA_GATTC_BTH_SCAN_PARAM_EVT 29 /* Batch scan param event */ +#define BTA_GATTC_BTH_SCAN_DIS_EVT 30 /* Disable batch scan event */ +#define BTA_GATTC_SCAN_FLT_CFG_EVT 31 /* Scan filter config event */ +#define BTA_GATTC_SCAN_FLT_PARAM_EVT 32 /* Param filter event */ +#define BTA_GATTC_SCAN_FLT_STATUS_EVT 33 /* Filter status event */ +#define BTA_GATTC_ADV_VSC_EVT 34 /* ADV VSC event */ typedef UINT8 tBTA_GATTC_EVT; @@ -151,7 +151,7 @@ typedef tGATT_IF tBTA_GATTC_IF; typedef struct { UINT16 unit; /* as UUIUD defined by SIG */ - UINT16 descr; /* as UUID as defined by SIG */ + UINT16 descr; /* as UUID as defined by SIG */ tGATT_FORMAT format; INT8 exp; UINT8 name_spc; /* The name space of the description */ @@ -165,7 +165,7 @@ typedef UINT16 tBTA_GATT_CLT_CHAR_CONFIG; /* characteristic descriptor: server configuration value */ #define BTA_GATT_SVR_CONFIG_NONE GATT_SVR_CONFIG_NONE /* 0x0000 */ -#define BTA_GATT_SVR_CONFIG_BROADCAST GATT_SVR_CONFIG_BROADCAST /* 0x0001 */ +#define BTA_GATT_SVR_CONFIG_BROADCAST GATT_SVR_CONFIG_BROADCAST /* 0x0001 */ typedef UINT16 tBTA_GATT_SVR_CHAR_CONFIG; /* Characteristic Aggregate Format attribute value @@ -367,8 +367,8 @@ typedef struct { // btla-specific -- typedef struct { - tBTA_GATTC_IF client_if; - BD_ADDR remote_bda; + tBTA_GATTC_IF client_if; + BD_ADDR remote_bda; } tBTA_GATTC_ENC_CMPL_CB; typedef union { @@ -395,7 +395,6 @@ typedef void (tBTA_GATTC_ENB_CBACK)(tBTA_GATT_STATUS status); /* Client callback function */ typedef void (tBTA_GATTC_CBACK)(tBTA_GATTC_EVT event, tBTA_GATTC *p_data); - /* GATT Server Data Structure */ /* Server callback function events */ #define BTA_GATTS_REG_EVT 0 @@ -419,6 +418,7 @@ typedef void (tBTA_GATTC_CBACK)(tBTA_GATTC_EVT event, tBTA_GATTC *p_data); #define BTA_GATTS_CLOSE_EVT 18 #define BTA_GATTS_LISTEN_EVT 19 #define BTA_GATTS_CONGEST_EVT 20 +#define BTA_GATTS_SET_ATTR_VAL_EVT 21 typedef UINT8 tBTA_GATTS_EVT; typedef tGATT_IF tBTA_GATTS_IF; @@ -434,20 +434,22 @@ typedef tGATT_IF tBTA_GATTS_IF; #define BTA_GATT_PERM_WRITE_SIGNED GATT_PERM_WRITE_SIGNED /* bit 7 - 0x0080 */ #define BTA_GATT_PERM_WRITE_SIGNED_MITM GATT_PERM_WRITE_SIGNED_MITM /* bit 8 - 0x0100 */ typedef UINT16 tBTA_GATT_PERM; +typedef tGATT_ATTR_VAL tBTA_GATT_ATTR_VAL; +typedef tGATTS_ATTR_CONTROL tBTA_GATTS_ATTR_CONTROL; #define BTA_GATTS_INVALID_APP 0xff #define BTA_GATTS_INVALID_IF 0 /* definition of characteristic properties */ -#define BTA_GATT_CHAR_PROP_BIT_BROADCAST GATT_CHAR_PROP_BIT_BROADCAST /* 0x01 */ -#define BTA_GATT_CHAR_PROP_BIT_READ GATT_CHAR_PROP_BIT_READ /* 0x02 */ -#define BTA_GATT_CHAR_PROP_BIT_WRITE_NR GATT_CHAR_PROP_BIT_WRITE_NR /* 0x04 */ -#define BTA_GATT_CHAR_PROP_BIT_WRITE GATT_CHAR_PROP_BIT_WRITE /* 0x08 */ -#define BTA_GATT_CHAR_PROP_BIT_NOTIFY GATT_CHAR_PROP_BIT_NOTIFY /* 0x10 */ -#define BTA_GATT_CHAR_PROP_BIT_INDICATE GATT_CHAR_PROP_BIT_INDICATE /* 0x20 */ -#define BTA_GATT_CHAR_PROP_BIT_AUTH GATT_CHAR_PROP_BIT_AUTH /* 0x40 */ -#define BTA_GATT_CHAR_PROP_BIT_EXT_PROP GATT_CHAR_PROP_BIT_EXT_PROP /* 0x80 */ +#define BTA_GATT_CHAR_PROP_BIT_BROADCAST GATT_CHAR_PROP_BIT_BROADCAST /* 0x01 */ +#define BTA_GATT_CHAR_PROP_BIT_READ GATT_CHAR_PROP_BIT_READ /* 0x02 */ +#define BTA_GATT_CHAR_PROP_BIT_WRITE_NR GATT_CHAR_PROP_BIT_WRITE_NR /* 0x04 */ +#define BTA_GATT_CHAR_PROP_BIT_WRITE GATT_CHAR_PROP_BIT_WRITE /* 0x08 */ +#define BTA_GATT_CHAR_PROP_BIT_NOTIFY GATT_CHAR_PROP_BIT_NOTIFY /* 0x10 */ +#define BTA_GATT_CHAR_PROP_BIT_INDICATE GATT_CHAR_PROP_BIT_INDICATE /* 0x20 */ +#define BTA_GATT_CHAR_PROP_BIT_AUTH GATT_CHAR_PROP_BIT_AUTH /* 0x40 */ +#define BTA_GATT_CHAR_PROP_BIT_EXT_PROP GATT_CHAR_PROP_BIT_EXT_PROP /* 0x80 */ typedef UINT8 tBTA_GATT_CHAR_PROP; #ifndef BTA_GATTC_CHAR_DESCR_MAX @@ -476,8 +478,8 @@ typedef tGATTS_SRV_CHG tBTA_GATTS_SRV_CHG; typedef tGATTS_SRV_CHG_REQ tBTA_GATTS_SRV_CHG_REQ; typedef tGATTS_SRV_CHG_RSP tBTA_GATTS_SRV_CHG_RSP; -#define BTA_GATT_TRANSPORT_LE GATT_TRANSPORT_LE -#define BTA_GATT_TRANSPORT_BR_EDR GATT_TRANSPORT_BR_EDR +#define BTA_GATT_TRANSPORT_LE GATT_TRANSPORT_LE +#define BTA_GATT_TRANSPORT_BR_EDR GATT_TRANSPORT_BR_EDR #define BTA_GATT_TRANSPORT_LE_BR_EDR GATT_TRANSPORT_LE_BR_EDR typedef UINT8 tBTA_GATT_TRANSPORT; @@ -539,6 +541,13 @@ typedef struct { // btla-specific -- } tBTA_GATTS_ADD_RESULT; +typedef struct{ + tBTA_GATTS_IF server_if; + UINT16 service_id; + UINT16 attr_id; + tBTA_GATT_STATUS status; +}tBAT_GATTS_ATTR_VAL_RESULT; + typedef struct { tBTA_GATTS_IF server_if; UINT16 service_id; @@ -566,17 +575,18 @@ typedef struct { /* GATTS callback data */ typedef union { - tBTA_GATTS_REG_OPER reg_oper; - tBTA_GATTS_CREATE create; - tBTA_GATTS_SRVC_OPER srvc_oper; - tBTA_GATT_STATUS status; /* BTA_GATTS_LISTEN_EVT */ - tBTA_GATTS_ADD_RESULT add_result; /* add included service: BTA_GATTS_ADD_INCL_SRVC_EVT - add char : BTA_GATTS_ADD_CHAR_EVT - add char descriptor: BTA_GATTS_ADD_CHAR_DESCR_EVT */ - tBTA_GATTS_REQ req_data; - tBTA_GATTS_CONN conn; /* BTA_GATTS_CONN_EVT */ - tBTA_GATTS_CONGEST congest; /* BTA_GATTS_CONGEST_EVT callback data */ - tBTA_GATTS_CONF confirm; /* BTA_GATTS_CONF_EVT callback data */ + tBTA_GATTS_REG_OPER reg_oper; + tBTA_GATTS_CREATE create; + tBTA_GATTS_SRVC_OPER srvc_oper; + tBTA_GATT_STATUS status; /* BTA_GATTS_LISTEN_EVT */ + tBTA_GATTS_ADD_RESULT add_result; /* add included service: BTA_GATTS_ADD_INCL_SRVC_EVT + add char : BTA_GATTS_ADD_CHAR_EVT + add char descriptor: BTA_GATTS_ADD_CHAR_DESCR_EVT */ + tBAT_GATTS_ATTR_VAL_RESULT attr_val; + tBTA_GATTS_REQ req_data; + tBTA_GATTS_CONN conn; /* BTA_GATTS_CONN_EVT */ + tBTA_GATTS_CONGEST congest; /* BTA_GATTS_CONGEST_EVT callback data */ + tBTA_GATTS_CONF confirm; /* BTA_GATTS_CONF_EVT callback data */ } tBTA_GATTS; /* GATTS enable callback function */ @@ -1193,8 +1203,9 @@ extern void BTA_GATTS_AddIncludeService(UINT16 service_id, UINT16 included_servi ** Returns None ** *******************************************************************************/ -extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, - tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property); +extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, + tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control); /******************************************************************************* ** @@ -1214,8 +1225,9 @@ extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_ ** *******************************************************************************/ extern void BTA_GATTS_AddCharDescriptor (UINT16 service_id, - tBTA_GATT_PERM perm, - tBT_UUID *p_descr_uuid); + tBTA_GATT_PERM perm, + tBT_UUID *p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control); /******************************************************************************* ** @@ -1296,6 +1308,38 @@ extern void BTA_GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, tBTA_GATT_STATUS status, tBTA_GATTS_RSP *p_msg); + +/******************************************************************************* +** +** Function BTA_SetAttributeValue +** +** Description This function is called to set the attribute value in the gatt database +** +** Parameters attr_handle - the attribute value handle. +** length - the value length which has been set to the attribute. +** value - the pointer to the value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value); + + +/******************************************************************************* +** +** Function BTA_GetAttributeValue +** +** Description This function is called to get the attribute value in the gatt database +** +** Parameters attr_handle - the attribute value handle. +** length - the value length which has been set to the attribute. +** value - the pointer to the value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value); + /******************************************************************************* ** ** Function BTA_GATTS_Open diff --git a/components/bt/bluedroid/bta/include/bta_gatts_int.h b/components/bt/bluedroid/bta/include/bta_gatts_int.h index f934c4ac6a..0f9e689ace 100644 --- a/components/bt/bluedroid/bta/include/bta_gatts_int.h +++ b/components/bt/bluedroid/bta/include/bta_gatts_int.h @@ -48,6 +48,7 @@ enum { BTA_GATTS_API_START_SRVC_EVT, BTA_GATTS_API_STOP_SRVC_EVT, BTA_GATTS_API_RSP_EVT, + BTA_GATTS_API_SET_ATTR_VAL_EVT, BTA_GATTS_API_OPEN_EVT, BTA_GATTS_API_CANCEL_OPEN_EVT, BTA_GATTS_API_CLOSE_EVT, @@ -91,19 +92,21 @@ typedef struct { tBT_UUID char_uuid; tBTA_GATT_PERM perm; tBTA_GATT_CHAR_PROP property; - + tBTA_GATTS_ATTR_CONTROL control; + tBTA_GATT_ATTR_VAL attr_val; } tBTA_GATTS_API_ADD_CHAR; typedef struct { BT_HDR hdr; UINT16 included_service_id; - } tBTA_GATTS_API_ADD_INCL_SRVC; typedef struct { - BT_HDR hdr; - tBT_UUID descr_uuid; - tBTA_GATT_PERM perm; + BT_HDR hdr; + tBT_UUID descr_uuid; + tBTA_GATT_PERM perm; + tBTA_GATTS_ATTR_CONTROL control; + tBTA_GATT_ATTR_VAL attr_val; } tBTA_GATTS_API_ADD_DESCR; typedef struct { @@ -121,6 +124,12 @@ typedef struct { tBTA_GATTS_RSP *p_rsp; } tBTA_GATTS_API_RSP; +typedef struct{ + BT_HDR hdr; + UINT16 length; + UINT8 *value; +}tBTA_GATTS_API_SET_ATTR_VAL; + typedef struct { BT_HDR hdr; tBTA_GATT_TRANSPORT transport; @@ -156,6 +165,7 @@ typedef union { tBTA_GATTS_API_START api_start; tBTA_GATTS_API_INDICATION api_indicate; tBTA_GATTS_API_RSP api_rsp; + tBTA_GATTS_API_SET_ATTR_VAL api_set_val; tBTA_GATTS_API_OPEN api_open; tBTA_GATTS_API_CANCEL_OPEN api_cancel_open; @@ -169,7 +179,7 @@ typedef struct { BOOLEAN in_use; tBT_UUID app_uuid; tBTA_GATTS_CBACK *p_cback; - tBTA_GATTS_IF gatt_if; + tBTA_GATTS_IF gatt_if; } tBTA_GATTS_RCB; /* service registration control block */ @@ -219,6 +229,8 @@ extern void bta_gatts_create_srvc(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_add_include_srvc(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_get_attr_value(UINT16 attr_handle, UINT16 *length, UINT8 **value); extern void bta_gatts_delete_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_start_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_stop_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); diff --git a/components/bt/bluedroid/btc/core/btc_main.c b/components/bt/bluedroid/btc/core/btc_main.c index 6fae3af435..323a81f75a 100644 --- a/components/bt/bluedroid/btc/core/btc_main.c +++ b/components/bt/bluedroid/btc/core/btc_main.c @@ -42,14 +42,14 @@ static void btc_sec_callback(tBTA_DM_SEC_EVT event, tBTA_DM_SEC *p_data) static void btc_enable_bluetooth(void) { if (BTA_EnableBluetooth(btc_sec_callback) != BTA_SUCCESS) { - future_ready(*btc_main_get_future_p(BTC_MAIN_ENABLE_FUTURE), FUTURE_SUCCESS); + future_ready(*btc_main_get_future_p(BTC_MAIN_ENABLE_FUTURE), FUTURE_FAIL); } } static void btc_disable_bluetooth(void) { if (BTA_DisableBluetooth() != BTA_SUCCESS) { - future_ready(*btc_main_get_future_p(BTC_MAIN_DISABLE_FUTURE), FUTURE_SUCCESS); + future_ready(*btc_main_get_future_p(BTC_MAIN_DISABLE_FUTURE), FUTURE_FAIL); } } diff --git a/components/bt/bluedroid/btc/core/btc_manage.c b/components/bt/bluedroid/btc/core/btc_manage.c index 46d20dc2d8..213c4939c1 100644 --- a/components/bt/bluedroid/btc/core/btc_manage.c +++ b/components/bt/bluedroid/btc/core/btc_manage.c @@ -20,7 +20,7 @@ #include "esp_bt_defs.h" #include "esp_gatt_defs.h" -static esp_profile_cb_t btc_profile_cb_tab[BTC_PID_NUM] = {}; +static void *btc_profile_cb_tab[BTC_PID_NUM] = {}; void esp_profile_cb_reset(void) { @@ -31,7 +31,7 @@ void esp_profile_cb_reset(void) } } -int btc_profile_cb_set(btc_pid_t profile_id, esp_profile_cb_t cb) +int btc_profile_cb_set(btc_pid_t profile_id, void *cb) { if (profile_id < 0 || profile_id >= BTC_PID_NUM) { return -1; @@ -42,7 +42,7 @@ int btc_profile_cb_set(btc_pid_t profile_id, esp_profile_cb_t cb) return 0; } -esp_profile_cb_t btc_profile_cb_get(btc_pid_t profile_id) +void *btc_profile_cb_get(btc_pid_t profile_id) { if (profile_id < 0 || profile_id >= BTC_PID_NUM) { return NULL; diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index b4ce0d95ca..a245f54d28 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -80,7 +80,7 @@ static bt_status_t btc_task_post(btc_msg_t *msg) return BT_STATUS_PARM_INVALID; } - if (xQueueSend(xBtcQueue, msg, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xBtcQueue, msg, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("Btc Post failed\n"); return BT_STATUS_BUSY; } @@ -120,7 +120,7 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg int btc_init(void) { xBtcQueue = xQueueCreate(BTC_TASK_QUEUE_NUM, sizeof(btc_msg_t)); - xTaskCreate(btc_task, "Btc_task", BTC_TASK_STACK_SIZE, NULL, BTC_TASK_PRIO, &xBtcTaskHandle); + xTaskCreatePinnedToCore(btc_task, "Btc_task", BTC_TASK_STACK_SIZE, NULL, BTC_TASK_PRIO, &xBtcTaskHandle, 0); /* TODO: initial the profile_tab */ diff --git a/components/bt/bluedroid/btc/include/btc_manage.h b/components/bt/bluedroid/btc/include/btc_manage.h index e8591caa8a..8789f543dd 100644 --- a/components/bt/bluedroid/btc/include/btc_manage.h +++ b/components/bt/bluedroid/btc/include/btc_manage.h @@ -22,7 +22,7 @@ /* reset gatt callback table */ void esp_profile_cb_reset(void); -int btc_profile_cb_set(btc_pid_t profile_id, esp_profile_cb_t cb); -esp_profile_cb_t btc_profile_cb_get(btc_pid_t profile_id); +int btc_profile_cb_set(btc_pid_t profile_id, void *cb); +void *btc_profile_cb_get(btc_pid_t profile_id); #endif /* __BTC_MANAGE_H__ */ diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_adv.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_adv.c deleted file mode 100644 index fcd20821fa..0000000000 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_adv.c +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2015-2016 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. - -#include "blufi_adv.h" - - -/******************************************************************************* -** -** Function BlufiConfigadvData -** -** Description This function is called to override the BTA default ADV parameters. -** -** adv_data: Pointer to User defined ADV data structure. This -** memory space can not be freed until p_adv_data_cback -** is received. -** p_adv_data_cback: set adv data complete callback. -** -** Returns None -** -*******************************************************************************/ -void BlufiBleConfigadvData(tBLUFI_BLE_ADV_DATA *adv_data, - tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback) -{ - tBTA_BLE_AD_MASK data_mask = 0; - if (adv_data->adv_name != NULL) { - data_mask |= BTM_BLE_AD_BIT_DEV_NAME; - BTA_DmSetDeviceName(adv_data->adv_name); - } - if (adv_data->ble_adv_data.int_range.low != 0 || - adv_data->ble_adv_data.int_range.hi != 0) { - data_mask |= BTM_BLE_AD_BIT_INT_RANGE; - } - - if (adv_data->ble_adv_data.p_manu != NULL) { - data_mask |= BTM_BLE_AD_BIT_MANU; - } - - if (adv_data->ble_adv_data.p_services != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE; - } - - if (adv_data->ble_adv_data.p_service_32b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_32; - } - - if (adv_data->ble_adv_data.p_services_128b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_128; - } - - if (adv_data->ble_adv_data.p_sol_services != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_SOL; - } - - if (adv_data->ble_adv_data.p_sol_service_32b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_32SOL; - } - - if (adv_data->ble_adv_data.p_sol_service_128b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_128SOL; - } - - if (adv_data->ble_adv_data.p_service_data != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_DATA; - } - - if (adv_data->ble_adv_data.appearance != 0) { - data_mask |= BTM_BLE_AD_BIT_APPEARANCE; - } - - if (adv_data->ble_adv_data.p_proprietary != NULL) { - data_mask |= BTM_BLE_AD_BIT_PROPRIETARY; - } - - if (adv_data->ble_adv_data.tx_power != 0) { - data_mask |= BTM_BLE_AD_BIT_TX_PWR; - } - - BTA_DmBleSetAdvConfig(data_mask, &(adv_data->ble_adv_data), p_adv_data_cback); -} - - -/******************************************************************************* -** -** Function BLUFI_BleSetScanRsp -** -** Description This function is called to override the app scan response. -** -** Parameters Pointer to User defined ADV data structure -** -** Returns None -** -*******************************************************************************/ -void BlufiBleSetScanRsp(tBLUFI_BLE_ADV_DATA *scan_rsp_data, - tBTA_SET_ADV_DATA_CMPL_CBACK *p_scan_rsp_data_cback) -{ - tBTA_BLE_AD_MASK data_mask = 0; - if (scan_rsp_data->adv_name != NULL) { - data_mask |= BTM_BLE_AD_BIT_DEV_NAME; - BTA_DmSetDeviceName(scan_rsp_data->adv_name); - } - if (scan_rsp_data->ble_adv_data.int_range.low != 0 || - scan_rsp_data->ble_adv_data.int_range.hi != 0) { - data_mask |= BTM_BLE_AD_BIT_INT_RANGE; - } - - if (scan_rsp_data->ble_adv_data.p_manu != NULL) { - data_mask |= BTM_BLE_AD_BIT_MANU; - } - - if (scan_rsp_data->ble_adv_data.p_services != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE; - } - - if (scan_rsp_data->ble_adv_data.p_service_32b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_32; - } - - if (scan_rsp_data->ble_adv_data.p_services_128b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_128; - } - - if (scan_rsp_data->ble_adv_data.p_sol_services != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_SOL; - } - - if (scan_rsp_data->ble_adv_data.p_sol_service_32b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_32SOL; - } - - if (scan_rsp_data->ble_adv_data.p_sol_service_128b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_128SOL; - } - - if (scan_rsp_data->ble_adv_data.p_service_data != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_DATA; - } - - if (scan_rsp_data->ble_adv_data.appearance != 0) { - data_mask |= BTM_BLE_AD_BIT_APPEARANCE; - } - - if (scan_rsp_data->ble_adv_data.p_proprietary != NULL) { - data_mask |= BTM_BLE_AD_BIT_PROPRIETARY; - } - - if (scan_rsp_data->ble_adv_data.tx_power != 0) { - data_mask |= BTM_BLE_AD_BIT_TX_PWR; - } - - BTA_DmBleSetScanRsp(data_mask, &(scan_rsp_data->ble_adv_data), p_scan_rsp_data_cback); -} - - diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c index 8acb9565c8..7866ca0f80 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c @@ -31,251 +31,250 @@ #include "btc_task.h" #include "btc_manage.h" -#include "blufi_adv.h" #include "blufi_int.h" -const char success_msg[] = "BLUFI_CONFIG_OK"; -const char failed_msg[] = "BLUFI_CONFIG_FAILED"; - -#define BTC_BLUFI_CB_TO_APP(_event, _param) ((esp_profile_cb_t)btc_profile_cb_get(BTC_PID_BLUFI))(_event, _param) +#include "esp_blufi_api.h" #define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] +//define the blufi serivce uuid +#define BLUFI_SERVICE_UUID 0xFFFF +//define the blufi Char uuid (PHONE to ESP32) +#define BLUFI_CHAR_P2E_UUID 0xFF01 +//define the blufi Char uuid (ESP32 to PHONE) +#define BLUFI_CHAR_E2P_UUID 0xFF02 +//define the blufi Descriptor uuid (ESP32 to PHONE) +#define BLUFI_DESCR_E2P_UUID GATT_UUID_CHAR_CLIENT_CONFIG +//define the blufi APP ID +#define BLUFI_APP_UUID 0xFFFF -UINT16 esp32_uuid = SVC_BLUFI_UUID; -UINT8 esp32_manu[17] = {0xff, 0x20, 0x14, 0x07, 0x22, 0x00, 0x02, 0x5B, 0x00, 0x33, 0x49, 0x31, 0x30, 0x4a, 0x30, 0x30, 0x31}; -tBTA_BLE_MANU p_esp32_manu = {sizeof(esp32_manu), esp32_manu}; /* manufacturer data */ +#define BLUFI_HDL_NUM 6 -tBTA_BLE_SERVICE esp32_service = { - 0x01, //only one service in the ijiazu button profile - false, - &esp32_uuid -}; /* 16 bits services */ +tBLUFI_ENV blufi_env; +static /* const */ tBT_UUID blufi_srvc_uuid = {LEN_UUID_16, {BLUFI_SERVICE_UUID}}; +static /* const */ tBT_UUID blufi_char_uuid_p2e = {LEN_UUID_16, {BLUFI_CHAR_P2E_UUID}}; +static /* const */ tBT_UUID blufi_char_uuid_e2p = {LEN_UUID_16, {BLUFI_CHAR_E2P_UUID}}; +static /* const */ tBT_UUID blufi_descr_uuid_e2p = {LEN_UUID_16, {BLUFI_DESCR_E2P_UUID}}; +static /* const */ tBT_UUID blufi_app_uuid = {LEN_UUID_16, {BLUFI_APP_UUID}}; -tBLUFI_BLE_ADV_DATA esp32_adv_data[ADV_SCAN_IDX_MAX] = { - [BLE_ADV_DATA_IDX] = { - .adv_name = "Espressif_008", - { - {0, 0}, - NULL, //no manufature data to be setting in the esp32 adervetisiing datas - &esp32_service, - NULL, //the 128 bits service uuid set to null(not used) - NULL, //the 32 bits Service UUID set to null(not used) - NULL, //16 bits services Solicitation UUIDs set to null(not used) - NULL, //List of 32 bit Service Solicitation UUIDs set to null(not used) - NULL, //List of 128 bit Service Solicitation UUIDs set to null(not used) - NULL, //proprietary data set to null(not used) - NULL, //service data set not null(no service data to be sent) - 0x0200, //device type : generic display - BTA_DM_GENERAL_DISC, // General discoverable. - 0xFE //the tx power value,defult value is 0 - }, - }, - - [BLE_SCAN_RSP_DATA_IDX] = { - .adv_name = NULL, - { - {0, 0}, - &p_esp32_manu, - NULL, - NULL, //the 128 bits service uuid set to null(not used) - NULL, //the 32 bits Service UUID set to null(not used) - NULL, //16 bits services Solicitation UUIDs set to null(not used) - NULL, //List of 32 bit Service Solicitation UUIDs set to null(not used) - NULL, //List of 128 bit Service Solicitation UUIDs set to null(not used) - NULL, //proprietary data set to null(not used) - NULL, //service data set not null(no service data to be sent) - 0x0000, //device type : generic display - 0x00, // General discoverable. - 0x00 - }, //the tx power value,defult value is 0 - } -}; - - - -static tBLUFI_CB_ENV blufi_cb_env; - - - -/***************************************************************************** -** Constants -*****************************************************************************/ +// static functions declare static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data); +static void btc_blufi_recv_handler(uint8_t *data, int len); +static void btc_blufi_send_ack(uint8_t seq); +static inline void btc_blufi_cb_to_app(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param) +{ + esp_blufi_event_cb_t btc_blufi_cb = (esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI); + if (btc_blufi_cb) { + btc_blufi_cb(event, param); + } +} -/******************************************************************************* -** -** Function blufi_create_service -** -** Description Create a Service for the blufi profile -** -** Returns NULL -** -*******************************************************************************/ static void blufi_create_service(void) { - tBTA_GATTS_IF server_if ; - tBT_UUID uuid = {LEN_UUID_16, {SVC_BLUFI_UUID}}; - UINT16 num_handle = BLUFI_HDL_NUM; - UINT8 inst = 0x00; - server_if = blufi_cb_env.gatt_if; - blufi_cb_env.inst_id = inst; - if (!blufi_cb_env.enabled) { + if (!blufi_env.enabled) { LOG_ERROR("blufi service added error."); return; } - BTA_GATTS_CreateService(server_if, &uuid, inst, num_handle, true); + blufi_env.srvc_inst = 0x00; + BTA_GATTS_CreateService(blufi_env.gatt_if, &blufi_srvc_uuid, blufi_env.srvc_inst, BLUFI_HDL_NUM, true); } - -/******************************************************************************* -** -** Function blufi_profile_cb -** -** Description the callback function after the profile has been register to the BTA manager module -** -** Returns NULL -** -*******************************************************************************/ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) { tBTA_GATTS_RSP rsp; - tBT_UUID uuid = {LEN_UUID_16, {SVC_BLUFI_UUID}}; - UINT8 *p_rec_data = NULL; - tBTA_GATT_STATUS status; LOG_DEBUG("blufi profile cb event = %x\n", event); + switch (event) { case BTA_GATTS_REG_EVT: - status = p_data->reg_oper.status; + LOG_DEBUG("REG: status %d, app_uuid %04x, gatt_if %d\n", p_data->reg_oper.status, p_data->reg_oper.uuid.uu.uuid16, p_data->reg_oper.server_if); - LOG_DEBUG("p_data->reg_oper.status = %x\n", p_data->reg_oper.status); - LOG_DEBUG("(p_data->reg_oper.uuid.uu.uuid16=%x\n", p_data->reg_oper.uuid.uu.uuid16); if (p_data->reg_oper.status != BTA_GATT_OK) { - LOG_ERROR("blufi profile register failed\n"); + LOG_ERROR("BLUFI profile register failed\n"); return; } - blufi_cb_env.gatt_if = p_data->reg_oper.server_if; - blufi_cb_env.enabled = true; - LOG_DEBUG("register complete: event=%d, status=%d, server_if=%d\n", - event, status, blufi_cb_env.gatt_if); - - LOG_DEBUG("set advertising parameters\n"); - //set the advertising data to the btm layer - BlufiBleConfigadvData(&esp32_adv_data[BLE_ADV_DATA_IDX], NULL); - //set the adversting data to the btm layer - BlufiBleSetScanRsp(&esp32_adv_data[BLE_SCAN_RSP_DATA_IDX], NULL); - BTA_GATTS_Listen(blufi_cb_env.gatt_if, true, NULL); + blufi_env.gatt_if = p_data->reg_oper.server_if; + blufi_env.enabled = true; //create the blufi service to the service data base. - if (p_data->reg_oper.uuid.uu.uuid16 == SVC_BLUFI_UUID) { + if (p_data->reg_oper.uuid.uu.uuid16 == BLUFI_APP_UUID) { + LOG_DEBUG("%s %d\n", __func__, __LINE__); blufi_create_service(); } break; case BTA_GATTS_READ_EVT: memset(&rsp, 0, sizeof(tBTA_GATTS_API_RSP)); rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle; - rsp.attr_value.len = 2; - //rsp.attr_value.value[0] = 0xde; - //rsp.attr_value.value[1] = 0xed; - //rsp.attr_value.value[2] = 0xbe; - //rsp.attr_value.value[3] = 0xef; + rsp.attr_value.len = 1; + rsp.attr_value.value[0] = 0x00; BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, p_data->req_data.status, &rsp); break; - case BTA_GATTS_WRITE_EVT: - BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + case BTA_GATTS_WRITE_EVT: { + if(p_data->req_data.p_data->write_req.is_prep) { + tBTA_GATT_STATUS status = GATT_SUCCESS; + + if (blufi_env.prepare_buf == NULL) { + blufi_env.prepare_buf = GKI_getbuf(BLUFI_PREPAIR_BUF_MAX_SIZE); + if (blufi_env.prepare_buf == NULL) { + LOG_ERROR("Blufi prep no mem\n"); + status = GATT_NO_RESOURCES; + } + } else { + if(p_data->req_data.p_data->write_req.offset > BLUFI_PREPAIR_BUF_MAX_SIZE) { + status = GATT_INVALID_OFFSET; + } else if ((p_data->req_data.p_data->write_req.offset + p_data->req_data.p_data->write_req.len) > BLUFI_PREPAIR_BUF_MAX_SIZE) { + status = GATT_INVALID_ATTR_LEN; + } + } + + memset(&rsp, 0, sizeof(tGATTS_RSP)); + rsp.attr_value.handle = p_data->req_data.p_data->write_req.handle; + rsp.attr_value.len = p_data->req_data.p_data->write_req.len; + rsp.attr_value.offset = p_data->req_data.p_data->write_req.offset; + memcpy(rsp.attr_value.value, p_data->req_data.p_data->write_req.value, p_data->req_data.p_data->write_req.len); + + LOG_DEBUG("prep write, len=%d, offset=%d\n", p_data->req_data.p_data->write_req.len, p_data->req_data.p_data->write_req.offset); + + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + status, &rsp); + + memcpy(blufi_env.prepare_buf + p_data->req_data.p_data->write_req.offset, + p_data->req_data.p_data->write_req.value, + p_data->req_data.p_data->write_req.len); + blufi_env.prepare_len += p_data->req_data.p_data->write_req.len; + + return; + } else { + LOG_DEBUG("norm write, len=%d, offset=%d\n", p_data->req_data.p_data->write_req.len, p_data->req_data.p_data->write_req.offset); + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, p_data->req_data.status, NULL); - - LOG_DEBUG("Received blufi data:"); - for (int i = 0; i < p_data->req_data.p_data->write_req.len; i++) { - LOG_DEBUG("%x", p_data->req_data.p_data->write_req.value[i]); } - LOG_DEBUG("\n"); - - if (p_data->req_data.p_data->write_req.handle == blufi_cb_env.blufi_inst.blufi_hdl) { - btc_msg_t msg; - struct blufi_recv_evt_param recv_data; - - memset(&recv_data, 0x00, sizeof(struct blufi_recv_evt_param)); - - p_rec_data = &p_data->req_data.p_data->write_req.value[0]; - recv_data.data_len = p_data->req_data.p_data->write_req.len; - memcpy(recv_data.data, p_rec_data, recv_data.data_len); - - msg.sig = BTC_SIG_API_CB; - msg.pid = BTC_PID_BLUFI; - msg.act = BTC_BLUFI_CB_ACT_RECV_DATA; - - btc_transfer_context(&msg, &recv_data, sizeof(struct blufi_recv_evt_param), NULL); + + if (p_data->req_data.p_data->write_req.handle == blufi_env.handle_char_p2e) { + btc_blufi_recv_handler(&p_data->req_data.p_data->write_req.value[0], + p_data->req_data.p_data->write_req.len); } + break; + } + case BTA_GATTS_EXEC_WRITE_EVT: + LOG_DEBUG("exec write exec %d\n", p_data->req_data.p_data->exec_write); + + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + GATT_SUCCESS, NULL); + + if (p_data->req_data.p_data->exec_write == GATT_PREP_WRITE_EXEC) { + btc_blufi_recv_handler(blufi_env.prepare_buf, blufi_env.prepare_len); + } + + if (blufi_env.prepare_buf) { + GKI_freebuf(blufi_env.prepare_buf); + blufi_env.prepare_buf = NULL; + } + break; case BTA_GATTS_CONF_EVT: + LOG_DEBUG("CONIRM EVT\n"); /* Nothing */ break; case BTA_GATTS_CREATE_EVT: - uuid.uu.uuid16 = CHAR_BLUFI_UUID; - blufi_cb_env.cur_srvc_id = p_data->create.service_id; - blufi_cb_env.is_primery = p_data->create.is_primary; - //start the blufi service after created - BTA_GATTS_StartService(p_data->create.service_id, BTA_GATT_TRANSPORT_LE); + blufi_env.handle_srvc = p_data->create.service_id; + //add the frist blufi characteristic --> write characteristic - BTA_GATTS_AddCharacteristic(blufi_cb_env.cur_srvc_id, &uuid, - (GATT_PERM_WRITE | GATT_PERM_READ), - (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE | GATT_CHAR_PROP_BIT_NOTIFY)); + BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_p2e, + (GATT_PERM_WRITE), + (GATT_CHAR_PROP_BIT_WRITE), + NULL, NULL); break; case BTA_GATTS_ADD_CHAR_EVT: - if (p_data->add_result.char_uuid.uu.uuid16 == CHAR_BLUFI_UUID) { - //save the att handle to the env - blufi_cb_env.blufi_inst.blufi_hdl = p_data->add_result.attr_id; + switch (p_data->add_result.char_uuid.uu.uuid16) { + case BLUFI_CHAR_P2E_UUID: /* Phone to ESP32 */ + blufi_env.handle_char_p2e = p_data->add_result.attr_id; - uuid.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; - BTA_GATTS_AddCharDescriptor (blufi_cb_env.cur_srvc_id, - (GATT_PERM_WRITE | GATT_PERM_WRITE), - &uuid); + BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_e2p, + (GATT_PERM_READ), + (GATT_PERM_READ | GATT_CHAR_PROP_BIT_NOTIFY), + NULL, NULL); + break; + case BLUFI_CHAR_E2P_UUID: /* ESP32 to Phone */ + blufi_env.handle_char_e2p = p_data->add_result.attr_id; + + BTA_GATTS_AddCharDescriptor (blufi_env.handle_srvc, + (GATT_PERM_READ | GATT_PERM_WRITE), + &blufi_descr_uuid_e2p, + NULL, NULL); + break; + default: + break; } break; case BTA_GATTS_ADD_CHAR_DESCR_EVT: { /* call init finish */ + esp_blufi_cb_param_t param; btc_msg_t msg; + blufi_env.handle_descr_e2p = p_data->add_result.attr_id; + //start the blufi service after created + BTA_GATTS_StartService(blufi_env.handle_srvc, BTA_GATT_TRANSPORT_LE); + msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; - msg.act = BTC_BLUFI_CB_ACT_INIT_FINISH; - btc_transfer_context(&msg, NULL, 0, NULL); + msg.act = ESP_BLUFI_EVENT_INIT_FINISH; + param.init_finish.state = ESP_BLUFI_INIT_OK; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); break; } - case BTA_GATTS_CONNECT_EVT: + case BTA_GATTS_CONNECT_EVT: { + btc_msg_t msg; + esp_blufi_cb_param_t param; + //set the connection flag to true - LOG_ERROR("\ndevice is connected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", + LOG_INFO("\ndevice is connected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if, p_data->conn.reason, p_data->conn.conn_id); - blufi_cb_env.conn_id = p_data->conn.conn_id; + memcpy(blufi_env.remote_bda, p_data->conn.remote_bda, sizeof(esp_bd_addr_t)); + blufi_env.conn_id = p_data->conn.conn_id; + blufi_env.is_connected = true; - /*return whether the remote device is currently connected*/ - int is_connected = BTA_DmGetConnectionState(p_data->conn.remote_bda); - LOG_DEBUG("is_connected=%d\n", is_connected); - BTA_DmBleBroadcast(0); //stop adv + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_BLE_CONNECT; + memcpy(param.connect.remote_bda, p_data->conn.remote_bda, sizeof(esp_bd_addr_t)); + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); break; - case BTA_GATTS_DISCONNECT_EVT: + } + case BTA_GATTS_DISCONNECT_EVT: { + btc_msg_t msg; + esp_blufi_cb_param_t param; + + blufi_env.is_connected = false; //set the connection flag to true - blufi_cb_env.connected = false; + LOG_INFO("\ndevice is disconnected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", + BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if, + p_data->conn.reason, p_data->conn.conn_id); + + memcpy(blufi_env.remote_bda, p_data->conn.remote_bda, sizeof(esp_bd_addr_t)); + blufi_env.conn_id = p_data->conn.conn_id; + blufi_env.is_connected = false; + blufi_env.recv_seq = blufi_env.send_seq = 0; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT; + memcpy(param.disconnect.remote_bda, p_data->conn.remote_bda, sizeof(esp_bd_addr_t)); + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); break; + } case BTA_GATTS_OPEN_EVT: break; case BTA_GATTS_CLOSE_EVT: - if (blufi_cb_env.connected && (blufi_cb_env.conn_id == p_data->conn.conn_id)) { - //set the connection channal congested flag to true - blufi_cb_env.congest = p_data->congest.congested; - } - break; - case BTA_GATTS_LISTEN_EVT: break; case BTA_GATTS_CONGEST_EVT: break; @@ -286,72 +285,594 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) static tGATT_STATUS btc_blufi_profile_init(void) { - tBT_UUID app_uuid = {LEN_UUID_16, {SVC_BLUFI_UUID}}; + esp_blufi_callbacks_t *store_p = blufi_env.cbs; - - if (blufi_cb_env.enabled) { - LOG_ERROR("blufi svc already initaliezd"); + if (blufi_env.enabled) { + LOG_ERROR("BLUFI already initialized"); return GATT_ERROR; - } else { - memset(&blufi_cb_env, 0, sizeof(tBLUFI_CB_ENV)); } + memset(&blufi_env, 0x0, sizeof(blufi_env)); + blufi_env.cbs = store_p; /* if set callback prior, restore the point */ - /* register the blufi profile to the BTA_GATTS module*/ - BTA_GATTS_AppRegister(&app_uuid, blufi_profile_cb); + /* register the BLUFI profile to the BTA_GATTS module*/ + BTA_GATTS_AppRegister(&blufi_app_uuid, blufi_profile_cb); return GATT_SUCCESS; } -static void blufi_msg_notify(UINT8 *blufi_msg, UINT8 len) +static tGATT_STATUS btc_blufi_profile_deinit(void) { - BOOLEAN conn_status = blufi_cb_env.connected; - UINT16 conn_id = blufi_cb_env.conn_id; - UINT16 attr_id = blufi_cb_env.blufi_inst.blufi_hdl; - //notify rsp==false; indicate rsp==true. - BOOLEAN rsp = false; - if (!conn_status && blufi_cb_env.congest) { - LOG_ERROR("the conneciton for blufi profile has been loss"); + esp_blufi_cb_param_t param; + btc_msg_t msg; + + if (!blufi_env.enabled) { + LOG_ERROR("BLUFI already initialized"); + return GATT_ERROR; + } + + BTA_GATTS_StopService(blufi_env.handle_srvc); + BTA_GATTS_DeleteService(blufi_env.handle_srvc); + /* register the BLUFI profile to the BTA_GATTS module*/ + BTA_GATTS_AppDeregister(blufi_env.gatt_if); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_DEINIT_FINISH; + param.deinit_finish.state = ESP_BLUFI_DEINIT_OK; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + + return GATT_SUCCESS; +} + +static void btc_blufi_send_notify(uint8_t *pkt, int pkt_len) +{ + UINT16 conn_id = blufi_env.conn_id; + UINT16 attr_id = blufi_env.handle_char_e2p; + bool rsp = false; + + BTA_GATTS_HandleValueIndication(conn_id, attr_id, pkt_len, + pkt, rsp); +} + +static void btc_blufi_recv_handler(uint8_t *data, int len) +{ + struct blufi_hdr *hdr = (struct blufi_hdr *)data; + uint16_t checksum; + int ret; + + if (hdr->seq != blufi_env.recv_seq) { + LOG_ERROR("%s seq %d is not expect %d\n", __func__, hdr->seq, blufi_env.recv_seq + 1); return; } - BTA_GATTS_HandleValueIndication (conn_id, attr_id, len, - blufi_msg, rsp); + blufi_env.recv_seq++; + + // first step, decrypt + if (BLUFI_FC_IS_ENC(hdr->fc) + && (blufi_env.cbs && blufi_env.cbs->decrypt_func)) { + ret = blufi_env.cbs->decrypt_func(hdr->seq, hdr->data, hdr->data_len); + if (ret != hdr->data_len) { /* enc must be success and enc len must equal to plain len */ + LOG_ERROR("%s decrypt error %d\n", __func__, ret); + return; + } + } + + // second step, check sum + if (BLUFI_FC_IS_CHECK(hdr->fc) + && (blufi_env.cbs && blufi_env.cbs->checksum_func)) { + checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2); + if (memcmp(&checksum, &hdr->data[hdr->data_len], 2) != 0) { + LOG_ERROR("%s checksum error %04x, pkt %04x\n", __func__, checksum, *(uint16_t *)&hdr->data[hdr->data_len]); + return; + } + } + + if (BLUFI_FC_IS_REQ_ACK(hdr->fc)) { + btc_blufi_send_ack(hdr->seq); + } + + if (BLUFI_FC_IS_FRAG(hdr->fc)) { + if (blufi_env.offset == 0) { + blufi_env.total_len = *(uint16_t *)(hdr->data); + blufi_env.aggr_buf = GKI_getbuf(blufi_env.total_len); + if (blufi_env.aggr_buf == NULL) { + LOG_ERROR("%s no mem, len %d\n", __func__, blufi_env.total_len); + return; + } + } + memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data + 2, hdr->data_len - 2); + blufi_env.offset += (hdr->data_len - 2); + } else { + if (blufi_env.offset > 0) { /* if previous pkt is frag */ + memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data, hdr->data_len); + + btc_blufi_protocol_handler(hdr->type, blufi_env.aggr_buf, blufi_env.total_len); + blufi_env.offset = 0; + GKI_freebuf(blufi_env.aggr_buf); + blufi_env.aggr_buf = NULL; + } else { + btc_blufi_protocol_handler(hdr->type, hdr->data, hdr->data_len); + blufi_env.offset = 0; + } + } +} +void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) +{ + struct blufi_hdr *hdr = NULL; + int remain_len = total_data_len; + uint16_t checksum; + int ret; + + while (remain_len > 0) { + if (remain_len > BLUFI_FRAG_DATA_MAX_LEN) { + hdr = GKI_getbuf(sizeof(struct blufi_hdr) + 2 + BLUFI_FRAG_DATA_MAX_LEN + 2); + if (hdr == NULL) { + LOG_ERROR("%s no mem\n", __func__); + return; + } + hdr->fc = 0x0; + hdr->data_len = BLUFI_FRAG_DATA_MAX_LEN + 2; + *(uint16_t *)hdr->data = remain_len; + memcpy(hdr->data + 2, &data[total_data_len - remain_len], BLUFI_FRAG_DATA_MAX_LEN); //copy first, easy for check sum + hdr->fc |= BLUFI_FC_FRAG; + } else { + hdr = GKI_getbuf(sizeof(struct blufi_hdr) + remain_len + 2); + if (hdr == NULL) { + LOG_ERROR("%s no mem\n", __func__); + return; + } + hdr->fc = 0x0; + hdr->data_len = remain_len; + memcpy(hdr->data, &data[total_data_len - remain_len], hdr->data_len); //copy first, easy for check sum + } + + hdr->type = type; + hdr->fc |= BLUFI_FC_DIR_E2P; + hdr->seq = blufi_env.send_seq++; + + if (BLUFI_TYPE_IS_CTRL(hdr->type)) { + if ((blufi_env.sec_mode & BLUFI_CTRL_SEC_MODE_CHECK_MASK) + && (blufi_env.cbs && blufi_env.cbs->checksum_func)) { + hdr->fc |= BLUFI_FC_CHECK; + checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2); + memcpy(&hdr->data[hdr->data_len], &checksum, 2); + } + } else if (!BLUFI_TYPE_IS_DATA_NEG(hdr->type)) { + if ((blufi_env.sec_mode & BLUFI_DATA_SEC_MODE_CHECK_MASK) + && (blufi_env.cbs && blufi_env.cbs->checksum_func)) { + hdr->fc |= BLUFI_FC_CHECK; + checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2); + memcpy(&hdr->data[hdr->data_len], &checksum, 2); + } + + if ((blufi_env.sec_mode & BLUFI_DATA_SEC_MODE_ENC_MASK) + && (blufi_env.cbs && blufi_env.cbs->encrypt_func)) { + ret = blufi_env.cbs->encrypt_func(hdr->seq, hdr->data, hdr->data_len); + if (ret == hdr->data_len) { /* enc must be success and enc len must equal to plain len */ + hdr->fc |= BLUFI_FC_ENC; + } else { + LOG_ERROR("%s encrypt error %d\n", __func__, ret); + GKI_freebuf(hdr); + return; + } + } + } + + if (hdr->fc & BLUFI_FC_FRAG) { + remain_len -= (hdr->data_len - 2); + } else { + remain_len -= hdr->data_len; + } + + btc_blufi_send_notify((uint8_t *)hdr, + ((hdr->fc & BLUFI_FC_CHECK) ? + hdr->data_len + sizeof(struct blufi_hdr) + 2 : + hdr->data_len + sizeof(struct blufi_hdr))); + + GKI_freebuf(hdr); + hdr = NULL; + } } -static void btc_blufi_config_success(void) +static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *info, int info_len) { - LOG_DEBUG("config success\n"); - blufi_msg_notify((uint8_t *)success_msg, strlen(success_msg)); + uint8_t type; + uint8_t *data; + int data_len; + uint8_t *p; + + data_len = info_len + 3; + p = data = GKI_getbuf(data_len); + if (data == NULL) { + return; + } + + type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP); + *p++ = opmode; + *p++ = sta_conn_state; + *p++ = softap_conn_num; + + if (info) { + if (info->sta_bssid_set) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID; + *p++ = 6; + memcpy(p, info->sta_bssid, 6); + p += 6; + } + if (info->sta_ssid) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_SSID; + *p++ = info->sta_ssid_len; + memcpy(p, info->sta_ssid, info->sta_ssid_len); + p += info->sta_ssid_len; + } + if (info->sta_passwd) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD; + *p++ = info->sta_passwd_len; + memcpy(p, info->sta_passwd, info->sta_passwd_len); + p += info->sta_passwd_len; + } + if (info->softap_ssid) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID; + *p++ = info->softap_ssid_len; + memcpy(p, info->softap_ssid, info->softap_ssid_len); + p += info->softap_ssid_len; + } + if (info->softap_passwd) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD; + *p++ = info->softap_passwd_len; + memcpy(p, info->softap_passwd, info->softap_passwd_len); + p += info->softap_passwd_len; + } + if (info->softap_authmode_set) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE; + *p++ = 1; + *p++ = info->softap_authmode; + } + if (info->softap_max_conn_num_set) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM; + *p++ = 1; + *p++ = info->softap_max_conn_num; + } + if (info->softap_channel_set) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM; + *p++ = 1; + *p++ = info->softap_channel; + } + } + if (p - data > data_len) { + LOG_ERROR("%s len error %d %d\n", __func__, (int)(p - data), data_len); + } + + btc_blufi_send_encap(type, data, data_len); + GKI_freebuf(data); } -static void btc_blufi_config_failed(void) +static void btc_blufi_send_ack(uint8_t seq) { - LOG_DEBUG("config faield\n"); - blufi_msg_notify((uint8_t *)failed_msg, strlen(failed_msg)); + uint8_t type; + uint8_t data; + + type = BLUFI_BUILD_TYPE(BLUFI_TYPE_CTRL, BLUFI_TYPE_CTRL_SUBTYPE_ACK); + data = seq; + + btc_blufi_send_encap(type, &data, 1); +} + +void btc_blufi_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_blufi_cb_param_t *dst = (esp_blufi_cb_param_t *) p_dest; + esp_blufi_cb_param_t *src = (esp_blufi_cb_param_t *) p_src; + + switch (msg->act) { + case ESP_BLUFI_EVENT_RECV_STA_SSID: + dst->sta_ssid.ssid = GKI_getbuf(src->sta_ssid.ssid_len); + if (dst->sta_ssid.ssid == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->sta_ssid.ssid, src->sta_ssid.ssid, src->sta_ssid.ssid_len); + break; + case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + dst->sta_passwd.passwd = GKI_getbuf(src->sta_passwd.passwd_len); + if (dst->sta_passwd.passwd == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->sta_passwd.passwd, src->sta_passwd.passwd, src->sta_passwd.passwd_len); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + dst->softap_ssid.ssid = GKI_getbuf(src->softap_ssid.ssid_len); + if (dst->softap_ssid.ssid == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->softap_ssid.ssid, src->softap_ssid.ssid, src->softap_ssid.ssid_len); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + dst->softap_passwd.passwd = GKI_getbuf(src->softap_passwd.passwd_len); + if (dst->softap_passwd.passwd == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->softap_passwd.passwd, src->softap_passwd.passwd, src->softap_passwd.passwd_len); + break; + case ESP_BLUFI_EVENT_RECV_USERNAME: + dst->username.name = GKI_getbuf(src->username.name_len); + if (dst->username.name == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->username.name, src->username.name, src->username.name_len); + break; + case ESP_BLUFI_EVENT_RECV_CA_CERT: + dst->ca.cert = GKI_getbuf(src->ca.cert_len); + if (dst->ca.cert == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->ca.cert, src->ca.cert, src->ca.cert_len); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + dst->client_cert.cert = GKI_getbuf(src->client_cert.cert_len); + if (dst->client_cert.cert == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->client_cert.cert, src->client_cert.cert, src->client_cert.cert_len); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + dst->server_cert.cert = GKI_getbuf(src->server_cert.cert_len); + if (dst->server_cert.cert == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->server_cert.cert, src->server_cert.cert, src->server_cert.cert_len); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + dst->client_pkey.pkey = GKI_getbuf(src->client_pkey.pkey_len); + if (dst->client_pkey.pkey == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->client_pkey.pkey, src->client_pkey.pkey, src->client_pkey.pkey_len); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + dst->server_pkey.pkey = GKI_getbuf(src->server_pkey.pkey_len); + if (dst->server_pkey.pkey == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->server_pkey.pkey, src->server_pkey.pkey, src->server_pkey.pkey_len); + break; + default: + break; + } +} + +void btc_blufi_cb_deep_free(btc_msg_t *msg) +{ + esp_blufi_cb_param_t *param = (esp_blufi_cb_param_t *)msg->arg; + + switch (msg->act) { + case ESP_BLUFI_EVENT_RECV_STA_SSID: + GKI_freebuf(param->sta_ssid.ssid); + break; + case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + GKI_freebuf(param->sta_passwd.passwd); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + GKI_freebuf(param->softap_ssid.ssid); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + GKI_freebuf(param->softap_passwd.passwd); + break; + case ESP_BLUFI_EVENT_RECV_USERNAME: + GKI_freebuf(param->username.name); + break; + case ESP_BLUFI_EVENT_RECV_CA_CERT: + GKI_freebuf(param->ca.cert); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + GKI_freebuf(param->client_cert.cert); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + GKI_freebuf(param->server_cert.cert); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + GKI_freebuf(param->client_pkey.pkey); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + GKI_freebuf(param->server_pkey.pkey); + break; + default: + break; + } } void btc_blufi_cb_handler(btc_msg_t *msg) { - esp_blufi_cb_param_t param; + esp_blufi_cb_param_t *param = (esp_blufi_cb_param_t *)msg->arg; switch (msg->act) { - case BTC_BLUFI_CB_ACT_INIT_FINISH: - param.init_finish.state = ESP_BLUFI_INIT_OK; - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_INIT_FINISH, ¶m); + case ESP_BLUFI_EVENT_INIT_FINISH: { + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_INIT_FINISH, param); break; - case BTC_BLUFI_CB_ACT_DEINIT_FINISH: - /* TODO: but now nothing */ + } + case ESP_BLUFI_EVENT_DEINIT_FINISH: { + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_DEINIT_FINISH, param); break; - case BTC_BLUFI_CB_ACT_RECV_DATA: - memcpy(¶m.recv_data, msg->arg, sizeof(struct blufi_recv_evt_param)); - - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_DATA, ¶m); + } + case ESP_BLUFI_EVENT_BLE_CONNECT: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_BLE_CONNECT, param); + break; + case ESP_BLUFI_EVENT_BLE_DISCONNECT: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_BLE_DISCONNECT, param); + break; + case ESP_BLUFI_EVENT_SET_WIFI_OPMODE: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_SET_WIFI_OPMODE, param); + break; + case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP, NULL); + break; + case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP, NULL); + break; + case ESP_BLUFI_EVENT_GET_WIFI_STATUS: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_GET_WIFI_STATUS, NULL); + break; + case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_DEAUTHENTICATE_STA, NULL); + break; + case ESP_BLUFI_EVENT_RECV_STA_BSSID: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_BSSID, param); + break; + case ESP_BLUFI_EVENT_RECV_STA_SSID: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_SSID, param); + break; + case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_PASSWD, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_SSID, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL, param); + break; + case ESP_BLUFI_EVENT_RECV_USERNAME: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_USERNAME, param); + break; + case ESP_BLUFI_EVENT_RECV_CA_CERT: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CA_CERT, param); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CLIENT_CERT, param); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SERVER_CERT, param); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY, param); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY, param); break; default: LOG_ERROR("%s UNKNOWN %d\n", __func__, msg->act); break; } + + btc_blufi_cb_deep_free(msg); +} + +void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_blufi_args_t *dst = (btc_blufi_args_t *) p_dest; + btc_blufi_args_t *src = (btc_blufi_args_t *) p_src; + + switch (msg->act) { + case BTC_BLUFI_ACT_SEND_CFG_REPORT: { + esp_blufi_extra_info_t *src_info = src->wifi_conn_report.extra_info; + dst->wifi_conn_report.extra_info_len = 0; + dst->wifi_conn_report.extra_info = NULL; + + if (src_info == NULL) { + return; + } + + dst->wifi_conn_report.extra_info = GKI_getbuf(sizeof(esp_blufi_extra_info_t)); + if (dst->wifi_conn_report.extra_info == NULL) { + return; + } + + if (src_info->sta_bssid_set) { + memcpy(dst->wifi_conn_report.extra_info->sta_bssid, src_info->sta_bssid, 6); + dst->wifi_conn_report.extra_info->sta_bssid_set = src_info->sta_bssid_set; + dst->wifi_conn_report.extra_info_len += (6 + 2); + } + if (src_info->sta_ssid) { + dst->wifi_conn_report.extra_info->sta_ssid = GKI_getbuf(src_info->sta_ssid_len); + if (dst->wifi_conn_report.extra_info->sta_ssid) { + memcpy(dst->wifi_conn_report.extra_info->sta_ssid, src_info->sta_ssid, src_info->sta_ssid_len); + dst->wifi_conn_report.extra_info->sta_ssid_len = src_info->sta_ssid_len; + dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->sta_ssid_len + 2); + } + } + if (src_info->sta_passwd) { + dst->wifi_conn_report.extra_info->sta_passwd = GKI_getbuf(src_info->sta_passwd_len); + if (dst->wifi_conn_report.extra_info->sta_passwd) { + memcpy(dst->wifi_conn_report.extra_info->sta_passwd, src_info->sta_passwd, src_info->sta_passwd_len); + dst->wifi_conn_report.extra_info->sta_passwd_len = src_info->sta_passwd_len; + dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->sta_passwd_len + 2); + } + } + if (src_info->softap_ssid) { + dst->wifi_conn_report.extra_info->softap_ssid = GKI_getbuf(src_info->softap_ssid_len); + if (dst->wifi_conn_report.extra_info->softap_ssid) { + memcpy(dst->wifi_conn_report.extra_info->softap_ssid, src_info->softap_ssid, src_info->softap_ssid_len); + dst->wifi_conn_report.extra_info->softap_ssid_len = src_info->softap_ssid_len; + dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->softap_ssid_len + 2); + } + } + if (src_info->softap_passwd) { + dst->wifi_conn_report.extra_info->softap_passwd = GKI_getbuf(src_info->softap_passwd_len); + if (dst->wifi_conn_report.extra_info->softap_passwd) { + memcpy(dst->wifi_conn_report.extra_info->softap_passwd, src_info->softap_passwd, src_info->softap_passwd_len); + dst->wifi_conn_report.extra_info->softap_passwd_len = src_info->softap_passwd_len; + dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->softap_passwd_len + 2); + } + } + if (src_info->softap_authmode_set) { + dst->wifi_conn_report.extra_info->softap_authmode_set = src_info->softap_authmode_set; + dst->wifi_conn_report.extra_info->softap_authmode = src_info->softap_authmode; + dst->wifi_conn_report.extra_info_len += (1 + 2); + } + if (src_info->softap_max_conn_num_set) { + dst->wifi_conn_report.extra_info->softap_max_conn_num_set = src_info->softap_max_conn_num_set; + dst->wifi_conn_report.extra_info->softap_max_conn_num = src_info->softap_max_conn_num; + dst->wifi_conn_report.extra_info_len += (1 + 2); + } + if (src_info->softap_channel_set) { + dst->wifi_conn_report.extra_info->softap_channel_set = src_info->softap_channel_set; + dst->wifi_conn_report.extra_info->softap_channel = src_info->softap_channel; + dst->wifi_conn_report.extra_info_len += (1 + 2); + } + break; + } + default: + break; + } +} + +void btc_blufi_call_deep_free(btc_msg_t *msg) +{ + btc_blufi_args_t *arg = (btc_blufi_args_t *)msg->arg; + + switch (msg->act) { + case BTC_BLUFI_ACT_SEND_CFG_REPORT: { + esp_blufi_extra_info_t *info = (esp_blufi_extra_info_t *)arg->wifi_conn_report.extra_info; + + if (info == NULL) { + return; + } + if (info->sta_ssid) { + GKI_freebuf(info->sta_ssid); + } + if (info->sta_passwd) { + GKI_freebuf(info->sta_passwd); + } + if (info->softap_ssid) { + GKI_freebuf(info->softap_ssid); + } + if (info->softap_passwd) { + GKI_freebuf(info->softap_passwd); + } + GKI_freebuf(info); + break; + } + default: + break; + } } void btc_blufi_call_handler(btc_msg_t *msg) @@ -363,17 +884,28 @@ void btc_blufi_call_handler(btc_msg_t *msg) btc_blufi_profile_init(); break; case BTC_BLUFI_ACT_DEINIT: - /* TODO: but now nothing */ + btc_blufi_profile_deinit(); break; - case BTC_BLUFI_ACT_SEND_CFG_STATE: - if (arg->cfg_state.state == ESP_BLUFI_CONFIG_OK) { - btc_blufi_config_success(); - } else { - btc_blufi_config_failed(); - } + case BTC_BLUFI_ACT_SEND_CFG_REPORT: + btc_blufi_wifi_conn_report(arg->wifi_conn_report.opmode, + arg->wifi_conn_report.sta_conn_state, + arg->wifi_conn_report.softap_conn_num, + arg->wifi_conn_report.extra_info, + arg->wifi_conn_report.extra_info_len); break; default: LOG_ERROR("%s UNKNOWN %d\n", __func__, msg->act); break; } + btc_blufi_call_deep_free(msg); +} + +void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks) +{ + blufi_env.cbs = callbacks; +} + +uint16_t btc_blufi_get_version(void) +{ + return BTC_BLUFI_VERSION; } diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c new file mode 100644 index 0000000000..d4d617f000 --- /dev/null +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c @@ -0,0 +1,249 @@ +// Copyright 2015-2016 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. + +#include + +#include +#include +#include + + +#include "bt_target.h" +#include "bt_trace.h" +#include "bt_types.h" +#include "gatt_api.h" +#include "bta_api.h" +#include "bta_gatt_api.h" +#include "bta_gatts_int.h" + +#include "btc_blufi_prf.h" +#include "btc_task.h" +#include "btc_manage.h" + +#include "blufi_int.h" + +#include "esp_wifi.h" + +extern tBLUFI_ENV blufi_env; + +void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) +{ + btc_msg_t msg; + esp_blufi_cb_param_t param; + uint8_t *output_data = NULL; + int output_len = 0; + bool need_free = false; + + switch (BLUFI_GET_TYPE(type)) { + case BLUFI_TYPE_CTRL: + switch (BLUFI_GET_SUBTYPE(type)) { + case BLUFI_TYPE_CTRL_SUBTYPE_ACK: + /* TODO: check sequence */ + break; + case BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE: + blufi_env.sec_mode = data[0]; + break; + case BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_SET_WIFI_OPMODE; + param.wifi_mode.op_mode = data[0]; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP; + + btc_transfer_context(&msg, NULL, 0, NULL); + break; + case BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP; + + btc_transfer_context(&msg, NULL, 0, NULL); + break; + case BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_GET_WIFI_STATUS; + + btc_transfer_context(&msg, NULL, 0, NULL); + break; + case BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_DEAUTHENTICATE_STA; + + btc_transfer_context(&msg, NULL, 0, NULL); + break; + case BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION: { + uint8_t type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION); + uint8_t data[2]; + + data[0] = BTC_BLUFI_GREAT_VER; + data[1] = BTC_BLUFI_SUB_VER; + btc_blufi_send_encap(type, &data[0], sizeof(data)); + break; + } + default: + LOG_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type); + break; + } + break; + case BLUFI_TYPE_DATA: + switch (BLUFI_GET_SUBTYPE(type)) { + case BLUFI_TYPE_DATA_SUBTYPE_NEG: + if (blufi_env.cbs && blufi_env.cbs->negotiate_data_handler) { + blufi_env.cbs->negotiate_data_handler(data, len, &output_data, &output_len, &need_free); + } + + if (output_data && output_len > 0) { + btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_NEG), + output_data, output_len); + } + break; + case BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_STA_BSSID; + memcpy(param.sta_bssid.bssid, &data[0], 6); + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_DATA_SUBTYPE_STA_SSID: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_STA_SSID; + param.sta_ssid.ssid = &data[0]; + param.sta_ssid.ssid_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_STA_PASSWD; + param.sta_passwd.passwd = &data[0]; + param.sta_passwd.passwd_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_SSID; + param.softap_ssid.ssid = &data[0]; + param.softap_ssid.ssid_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD; + param.softap_passwd.passwd = &data[0]; + param.softap_passwd.passwd_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM; + param.softap_max_conn_num.max_conn_num = data[0]; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE; + param.softap_auth_mode.auth_mode = data[0]; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL; + param.softap_channel.channel = data[0]; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_DATA_SUBTYPE_USERNAME: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_USERNAME; + param.username.name = &data[0]; + param.username.name_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_CA: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_CA_CERT; + param.ca.cert = &data[0]; + param.ca.cert_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_CLIENT_CERT; + param.client_cert.cert = &data[0]; + param.client_cert.cert_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SERVER_CERT; + param.client_cert.cert = &data[0]; + param.client_cert.cert_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY; + param.client_pkey.pkey = &data[0]; + param.client_pkey.pkey_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY; + param.client_pkey.pkey = &data[0]; + param.client_pkey.pkey_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + default: + LOG_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type); + break; + } + break; + default: + break; + } +} diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_adv.h b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_adv.h deleted file mode 100644 index 39869ec9e4..0000000000 --- a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_adv.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015-2016 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 __BLUFI_ADV_H__ -#define __BLUFI_ADV_H__ - -#include "bta_api.h" -#include "btm_ble_api.h" -#include "esp_bt_defs.h" - -typedef enum { - BLE_ADV_DATA_IDX = 0, - BLE_SCAN_RSP_DATA_IDX = 1, - ADV_SCAN_IDX_MAX, -} ADV_SCAN_IDX_t; - -typedef struct { - char *adv_name; //set the device name to be sent on the advertising - tBTA_BLE_ADV_DATA ble_adv_data; -} tBLUFI_BLE_ADV_DATA; - -extern void BlufiBleConfigadvData(tBLUFI_BLE_ADV_DATA *adv_data, - tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback); - -extern void BlufiBleSetScanRsp(tBLUFI_BLE_ADV_DATA *scan_rsp_data, - tBTA_SET_ADV_DATA_CMPL_CBACK *p_scan_rsp_data_cback); - -#endif /* __BLUFI_ADV_H__ */ diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h index f46a41a2e4..c21b41c4ca 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h +++ b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h @@ -15,38 +15,162 @@ #ifndef __BLUFI_INT_H__ #define __BLUFI_INT_H__ -//define the blufi serivce uuid -#define SVC_BLUFI_UUID 0xFFFF -//define the blufi Char uuid -#define CHAR_BLUFI_UUID 0xFF01 - -#define BLUFI_HDL_NUM 4 - -#define BLUFI_VAL_MAX_LEN (128) - -#define BLUFI_MAX_STRING_DATA 128 - - -typedef struct { - UINT8 app_id; - UINT16 blufi_hdl; -} tBLUFI_INST; - +#define BTC_BLUFI_GREAT_VER 0x01 //Version + Subversion +#define BTC_BLUFI_SUB_VER 0x00 //Version + Subversion +#define BTC_BLUFI_VERSION ((BTC_BLUFI_GREAT_VER<<8)|BTC_BLUFI_SUB_VER) //Version + Subversion /* service engine control block */ typedef struct { - BOOLEAN enabled; - BOOLEAN is_primery; - UINT8 inst_id; + /* Protocol reference */ tGATT_IF gatt_if; - tBLUFI_INST blufi_inst; - BOOLEAN in_use; - BOOLEAN congest; + UINT8 srvc_inst; + UINT16 handle_srvc; + UINT16 handle_char_p2e; + UINT16 handle_char_e2p; + UINT16 handle_descr_e2p; UINT16 conn_id; - BOOLEAN connected; + BOOLEAN is_connected; BD_ADDR remote_bda; UINT32 trans_id; - UINT8 cur_srvc_id; -} tBLUFI_CB_ENV; + UINT8 congest; +#define BLUFI_PREPAIR_BUF_MAX_SIZE 1024 + uint8_t *prepare_buf; + int prepare_len; + /* Control reference */ + esp_blufi_callbacks_t *cbs; + BOOLEAN enabled; + uint8_t send_seq; + uint8_t recv_seq; + uint8_t sec_mode; + uint8_t *aggr_buf; + uint16_t total_len; + uint16_t offset; +} tBLUFI_ENV; + +/* BLUFI protocol */ +struct blufi_hdr{ + uint8_t type; + uint8_t fc; + uint8_t seq; + uint8_t data_len; + uint8_t data[0]; +}; +typedef struct blufi_hdr blufi_hd_t; + +struct blufi_frag_hdr { + uint8_t type; + uint8_t fc; + uint8_t seq; + uint8_t data_len; + uint16_t total_len; + uint8_t content[0]; +}; +typedef struct blufi_frag_hdr blufi_frag_hdr_t; + +#define BLUFI_DATA_SEC_MODE_CHECK_MASK 0x01 +#define BLUFI_DATA_SEC_MODE_ENC_MASK 0x02 +#define BLUFI_CTRL_SEC_MODE_CHECK_MASK 0x10 +#define BLUFI_CTRL_SEC_MODE_ENC_MASK 0x20 + +// packet type +#define BLUFI_TYPE_MASK 0x03 +#define BLUFI_TYPE_SHIFT 0 +#define BLUFI_SUBTYPE_MASK 0xFC +#define BLUFI_SUBTYPE_SHIFT 2 + +#define BLUFI_GET_TYPE(type) ((type) & BLUFI_TYPE_MASK) +#define BLUFI_GET_SUBTYPE(type) (((type) & BLUFI_SUBTYPE_MASK) >>BLUFI_SUBTYPE_SHIFT) +#define BLUFI_BUILD_TYPE(type, subtype) (((type) & BLUFI_TYPE_MASK) | ((subtype)<flag != 0) { mask = BTM_BLE_AD_BIT_FLAGS; + bta_adv_data->flag = p_adv_data->flag; } if (p_adv_data->include_name) { @@ -200,7 +206,7 @@ static void btc_to_bta_adv_data(esp_ble_adv_data_t *p_adv_data, tBTA_BLE_ADV_DAT } if (NULL != bta_adv_data->p_services->p_uuid) { - LOG_ERROR("%s - In 16-UUID_data", __FUNCTION__); + LOG_DEBUG("%s - In 16-UUID_data", __FUNCTION__); mask |= BTM_BLE_AD_BIT_SERVICE; ++bta_adv_data->p_services->num_service; *p_uuid_out16++ = bt_uuid.uu.uuid16; @@ -220,7 +226,7 @@ static void btc_to_bta_adv_data(esp_ble_adv_data_t *p_adv_data, tBTA_BLE_ADV_DAT } if (NULL != bta_adv_data->p_service_32b->p_uuid) { - LOG_ERROR("%s - In 32-UUID_data", __FUNCTION__); + LOG_DEBUG("%s - In 32-UUID_data", __FUNCTION__); mask |= BTM_BLE_AD_BIT_SERVICE_32; ++bta_adv_data->p_service_32b->num_service; *p_uuid_out32++ = bt_uuid.uu.uuid32; @@ -503,16 +509,16 @@ void btc_gap_ble_cb_handler(btc_msg_t *msg) switch (msg->act) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: - BTC_GAP_BLE_CB_TO_APP(ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT, param); + btc_gap_ble_cb_to_app(ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT, param); break; case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT : - BTC_GAP_BLE_CB_TO_APP(ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT, param); + btc_gap_ble_cb_to_app(ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT, param); break; case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: - BTC_GAP_BLE_CB_TO_APP(ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, param); + btc_gap_ble_cb_to_app(ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, param); break; case ESP_GAP_BLE_SCAN_RESULT_EVT: - BTC_GAP_BLE_CB_TO_APP(ESP_GAP_BLE_SCAN_RESULT_EVT, param); + btc_gap_ble_cb_to_app(ESP_GAP_BLE_SCAN_RESULT_EVT, param); break; default: break; diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c index 938d2dc409..217ec9db4b 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c @@ -169,13 +169,14 @@ uint16_t get_uuid16(tBT_UUID *p_uuid) return (UINT16) p_uuid->uu.uuid32; } } -uint16_t set_read_value(esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src) +uint16_t set_read_value(uint8_t *gattc_if, esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src) { uint16_t descr_type = 0; uint16_t len = 0; p_dest->read.status = p_src->status; - p_dest->read.conn_id = p_src->conn_id; + p_dest->read.conn_id = BTC_GATT_GET_CONN_ID(p_src->conn_id); + *gattc_if = BTC_GATT_GET_GATT_IF(p_src->conn_id); bta_to_btc_srvc_id(&p_dest->read.srvc_id, &p_src->srvc_id); bta_to_btc_gatt_id(&p_dest->read.char_id, &p_src->char_id); bta_to_btc_gatt_id(&p_dest->read.descr_id, &p_src->descr_type); diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c index b5da7ed249..537be9092c 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c @@ -22,7 +22,13 @@ #include "bt_trace.h" #include "esp_gattc_api.h" -#define BTC_GATTC_CB_TO_APP(_event, _param) ((esp_profile_cb_t )btc_profile_cb_get(BTC_PID_GATTC))(_event, _param) +static inline void btc_gattc_cb_to_app(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + esp_gattc_cb_t btc_gattc_cb = (esp_gattc_cb_t )btc_profile_cb_get(BTC_PID_GATTC); + if (btc_gattc_cb) { + btc_gattc_cb(event, gattc_if, param); + } +} void btc_gattc_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) { @@ -141,13 +147,13 @@ static void btc_gattc_app_register(btc_ble_gattc_args_t *arg) static void btc_gattc_app_unregister(btc_ble_gattc_args_t *arg) { - BTA_GATTC_AppDeregister(arg->app_unreg.gatt_if); + BTA_GATTC_AppDeregister(arg->app_unreg.gattc_if); } static void btc_gattc_open(btc_ble_gattc_args_t *arg) { tBTA_GATT_TRANSPORT transport = BTA_GATT_TRANSPORT_LE; - BTA_GATTC_Open(arg->open.gatt_if, arg->open.remote_bda, arg->open.is_direct, transport); + BTA_GATTC_Open(arg->open.gattc_if, arg->open.remote_bda, arg->open.is_direct, transport); } static void btc_gattc_close(btc_ble_gattc_args_t *arg) @@ -181,6 +187,7 @@ static void btc_gattc_get_first_char(btc_ble_gattc_args_t *arg) tBTA_GATT_CHAR_PROP out_char_prop; tBTA_GATT_SRVC_ID srvc_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&srvc_id, &arg->get_first_char.service_id); status = BTA_GATTC_GetFirstChar(arg->get_first_char.conn_id, &srvc_id, NULL, @@ -189,13 +196,14 @@ static void btc_gattc_get_first_char(btc_ble_gattc_args_t *arg) bta_to_btc_gatt_id(&char_id, &out_char_id.char_id); } + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_first_char.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_char.conn_id = arg->get_first_char.conn_id; + param.get_char.conn_id = BTC_GATT_GET_CONN_ID(arg->get_first_char.conn_id); param.get_char.status = status; memcpy(¶m.get_char.srvc_id, &arg->get_first_char.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_char.char_id, &char_id, sizeof(esp_gatt_id_t)); param.get_char.char_prop = out_char_prop; - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_CHAR_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_CHAR_EVT, gattc_if, ¶m); } static void btc_gattc_get_next_char(btc_ble_gattc_args_t *arg) @@ -206,6 +214,8 @@ static void btc_gattc_get_next_char(btc_ble_gattc_args_t *arg) tBTA_GATTC_CHAR_ID out_char_id; tBTA_GATT_CHAR_PROP out_char_prop; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; + btc_to_bta_srvc_id(&in_char_id.srvc_id, &arg->get_next_char.service_id); btc_to_bta_gatt_id(&in_char_id.char_id, &arg->get_next_char.char_id); @@ -216,13 +226,14 @@ static void btc_gattc_get_next_char(btc_ble_gattc_args_t *arg) bta_to_btc_gatt_id(&char_id, &out_char_id.char_id); } + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_next_char.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_char.conn_id = arg->get_next_char.conn_id; + param.get_char.conn_id = BTC_GATT_GET_CONN_ID(arg->get_next_char.conn_id); param.get_char.status = status; memcpy(¶m.get_char.srvc_id, &arg->get_next_char.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_char.char_id, &char_id, sizeof(esp_gatt_id_t)); param.get_char.char_prop = out_char_prop; - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_CHAR_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_CHAR_EVT, gattc_if, ¶m); } static void btc_gattc_get_first_descr(btc_ble_gattc_args_t *arg) @@ -232,6 +243,7 @@ static void btc_gattc_get_first_descr(btc_ble_gattc_args_t *arg) tBTA_GATTC_CHAR_ID in_char_id; tBTA_GATTC_CHAR_DESCR_ID out_char_descr_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&in_char_id.srvc_id, &arg->get_first_descr.service_id); btc_to_bta_gatt_id(&in_char_id.char_id, &arg->get_first_descr.char_id); @@ -243,13 +255,14 @@ static void btc_gattc_get_first_descr(btc_ble_gattc_args_t *arg) bta_to_btc_gatt_id(&descr_id, &out_char_descr_id.descr_id); } + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_first_descr.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_descr.conn_id = arg->get_first_descr.conn_id; + param.get_descr.conn_id = BTC_GATT_GET_CONN_ID(arg->get_first_descr.conn_id); param.get_descr.status = status; memcpy(¶m.get_descr.srvc_id, &arg->get_first_descr.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_descr.char_id, &arg->get_first_descr.char_id, sizeof(esp_gatt_id_t)); memcpy(¶m.get_descr.descr_id, &descr_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_DESCR_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_DESCR_EVT, gattc_if, ¶m); } static void btc_gattc_get_next_descr(btc_ble_gattc_args_t *arg) @@ -259,6 +272,7 @@ static void btc_gattc_get_next_descr(btc_ble_gattc_args_t *arg) tBTA_GATTC_CHAR_DESCR_ID in_char_descr_id; tBTA_GATTC_CHAR_DESCR_ID out_char_descr_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&in_char_descr_id.char_id.srvc_id, &arg->get_next_descr.service_id); btc_to_bta_gatt_id(&in_char_descr_id.char_id.char_id, &arg->get_next_descr.char_id); @@ -270,13 +284,14 @@ static void btc_gattc_get_next_descr(btc_ble_gattc_args_t *arg) bta_to_btc_gatt_id(&descr_id, &out_char_descr_id.descr_id); } + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_next_descr.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_descr.conn_id = arg->get_next_descr.conn_id; + param.get_descr.conn_id = BTC_GATT_GET_CONN_ID(arg->get_next_descr.conn_id); param.get_descr.status = status; memcpy(¶m.get_descr.srvc_id, &arg->get_next_descr.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_descr.char_id, &arg->get_next_descr.char_id, sizeof(esp_gatt_id_t)); memcpy(¶m.get_descr.descr_id, &descr_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_DESCR_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_DESCR_EVT, gattc_if, ¶m); } static void btc_gattc_get_first_incl_service(btc_ble_gattc_args_t *arg) @@ -286,6 +301,7 @@ static void btc_gattc_get_first_incl_service(btc_ble_gattc_args_t *arg) tBTA_GATT_SRVC_ID srvc_id; tBTA_GATTC_INCL_SVC_ID out_incl_svc_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&srvc_id, &arg->get_first_incl_srvc.service_id); @@ -294,12 +310,13 @@ static void btc_gattc_get_first_incl_service(btc_ble_gattc_args_t *arg) bta_to_btc_srvc_id(&incl_srvc_id, &out_incl_svc_id.incl_svc_id); + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_first_incl_srvc.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_incl_srvc.conn_id = arg->get_first_incl_srvc.conn_id; + param.get_incl_srvc.conn_id = BTC_GATT_GET_CONN_ID(arg->get_first_incl_srvc.conn_id); param.get_incl_srvc.status = status; memcpy(¶m.get_incl_srvc.srvc_id, &arg->get_first_incl_srvc.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_incl_srvc.incl_srvc_id, &incl_srvc_id, sizeof(esp_gatt_srvc_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_INCL_SRVC_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_INCL_SRVC_EVT, gattc_if, ¶m); } static void btc_gattc_get_next_incl_service(btc_ble_gattc_args_t *arg) @@ -309,6 +326,7 @@ static void btc_gattc_get_next_incl_service(btc_ble_gattc_args_t *arg) tBTA_GATTC_INCL_SVC_ID in_incl_svc_id; tBTA_GATTC_INCL_SVC_ID out_incl_svc_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&in_incl_svc_id.srvc_id, &arg->get_next_incl_srvc.service_id); btc_to_bta_srvc_id(&in_incl_svc_id.incl_svc_id, &arg->get_next_incl_srvc.start_service_id); @@ -318,12 +336,13 @@ static void btc_gattc_get_next_incl_service(btc_ble_gattc_args_t *arg) bta_to_btc_srvc_id(&incl_srvc_id, &out_incl_svc_id.incl_svc_id); + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_next_incl_srvc.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_incl_srvc.conn_id = arg->get_next_incl_srvc.conn_id; + param.get_incl_srvc.conn_id = BTC_GATT_GET_CONN_ID(arg->get_next_incl_srvc.conn_id); param.get_incl_srvc.status = status; memcpy(¶m.get_incl_srvc.srvc_id, &arg->get_next_incl_srvc.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_incl_srvc.incl_srvc_id, &incl_srvc_id, sizeof(esp_gatt_srvc_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_INCL_SRVC_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_INCL_SRVC_EVT, gattc_if, ¶m); } static void btc_gattc_read_char(btc_ble_gattc_args_t *arg) @@ -402,15 +421,15 @@ static void btc_gattc_reg_for_notify(btc_ble_gattc_args_t *arg) btc_to_bta_srvc_id(&in_char_id.srvc_id, &arg->reg_for_notify.service_id); btc_to_bta_gatt_id(&in_char_id.char_id, &arg->reg_for_notify.char_id); - status = BTA_GATTC_RegisterForNotifications(arg->reg_for_notify.gatt_if, + status = BTA_GATTC_RegisterForNotifications(arg->reg_for_notify.gattc_if, arg->reg_for_notify.remote_bda, &in_char_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); param.reg_for_notify.status = status; memcpy(¶m.reg_for_notify.srvc_id, &arg->reg_for_notify.service_id, sizeof(esp_gatt_srvc_id_t)); - memcpy(¶m.reg_for_notify.char_id, &arg->reg_for_notify.service_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_REG_FOR_NOTIFY_EVT, ¶m); + memcpy(¶m.reg_for_notify.char_id, &arg->reg_for_notify.char_id, sizeof(esp_gatt_id_t)); + btc_gattc_cb_to_app(ESP_GATTC_REG_FOR_NOTIFY_EVT, arg->reg_for_notify.gattc_if, ¶m); } static void btc_gattc_unreg_for_notify(btc_ble_gattc_args_t *arg) @@ -422,7 +441,7 @@ static void btc_gattc_unreg_for_notify(btc_ble_gattc_args_t *arg) btc_to_bta_srvc_id(&in_char_id.srvc_id, &arg->unreg_for_notify.service_id); btc_to_bta_gatt_id(&in_char_id.char_id, &arg->unreg_for_notify.char_id); - status = BTA_GATTC_DeregisterForNotifications(arg->unreg_for_notify.gatt_if, + status = BTA_GATTC_DeregisterForNotifications(arg->unreg_for_notify.gattc_if, arg->unreg_for_notify.remote_bda, &in_char_id); @@ -430,7 +449,7 @@ static void btc_gattc_unreg_for_notify(btc_ble_gattc_args_t *arg) param.unreg_for_notify.status = status; memcpy(¶m.unreg_for_notify.srvc_id, &arg->unreg_for_notify.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.unreg_for_notify.char_id, &arg->unreg_for_notify.service_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_UNREG_FOR_NOTIFY_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_UNREG_FOR_NOTIFY_EVT, arg->unreg_for_notify.gattc_if, ¶m); } void btc_gattc_call_handler(btc_msg_t *msg) @@ -508,6 +527,7 @@ void btc_gattc_call_handler(btc_msg_t *msg) void btc_gattc_cb_handler(btc_msg_t *msg) { tBTA_GATTC *arg = (tBTA_GATTC *)(msg->arg); + esp_gatt_if_t gattc_if; esp_ble_gattc_cb_param_t param; memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); @@ -515,75 +535,90 @@ void btc_gattc_cb_handler(btc_msg_t *msg) switch (msg->act) { case BTA_GATTC_REG_EVT: { tBTA_GATTC_REG *reg_oper = &arg->reg_oper; + + gattc_if = reg_oper->client_if; param.reg.status = reg_oper->status; - param.reg.gatt_if = reg_oper->client_if; param.reg.app_id = reg_oper->app_uuid.uu.uuid16; - BTC_GATTC_CB_TO_APP(ESP_GATTC_REG_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_REG_EVT, gattc_if, ¶m); break; } case BTA_GATTC_DEREG_EVT: { - BTC_GATTC_CB_TO_APP(ESP_GATTC_UNREG_EVT, NULL); + tBTA_GATTC_REG *reg_oper = &arg->reg_oper; + + gattc_if = reg_oper->client_if; + btc_gattc_cb_to_app(ESP_GATTC_UNREG_EVT, gattc_if, NULL); break; } case BTA_GATTC_READ_CHAR_EVT: { - set_read_value(¶m, &arg->read); - BTC_GATTC_CB_TO_APP(ESP_GATTC_READ_CHAR_EVT, ¶m); + set_read_value(&gattc_if, ¶m, &arg->read); + btc_gattc_cb_to_app(ESP_GATTC_READ_CHAR_EVT, gattc_if, ¶m); break; } - case BTA_GATTC_WRITE_CHAR_EVT: case BTA_GATTC_PREP_WRITE_EVT: { tBTA_GATTC_WRITE *write = &arg->write; uint32_t ret_evt = (msg->act == BTA_GATTC_WRITE_CHAR_EVT) ? ESP_GATTC_WRITE_CHAR_EVT : ESP_GATTC_PREP_WRITE_EVT; - param.write.conn_id = write->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(write->conn_id); + param.write.conn_id = BTC_GATT_GET_CONN_ID(write->conn_id); param.write.status = write->status; bta_to_btc_srvc_id(¶m.write.srvc_id, &write->srvc_id); bta_to_btc_gatt_id(¶m.write.char_id, &write->char_id); - BTC_GATTC_CB_TO_APP(ret_evt, ¶m); + btc_gattc_cb_to_app(ret_evt, gattc_if, ¶m); break; } case BTA_GATTC_EXEC_EVT: { tBTA_GATTC_EXEC_CMPL *exec_cmpl = &arg->exec_cmpl; - param.exec_cmpl.conn_id = exec_cmpl->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(exec_cmpl->conn_id); + param.exec_cmpl.conn_id = BTC_GATT_GET_CONN_ID(exec_cmpl->conn_id); param.exec_cmpl.status = exec_cmpl->status; - BTC_GATTC_CB_TO_APP(ESP_GATTC_EXEC_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_EXEC_EVT, gattc_if, ¶m); break; } case BTA_GATTC_SEARCH_CMPL_EVT: { tBTA_GATTC_SEARCH_CMPL *search_cmpl = &arg->search_cmpl; - param.search_cmpl.conn_id = search_cmpl->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(search_cmpl->conn_id); + param.search_cmpl.conn_id = BTC_GATT_GET_CONN_ID(search_cmpl->conn_id); param.search_cmpl.status = search_cmpl->status; - BTC_GATTC_CB_TO_APP(ESP_GATTC_SEARCH_CMPL_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_SEARCH_CMPL_EVT, gattc_if, ¶m); break; } case BTA_GATTC_SEARCH_RES_EVT: { tBTA_GATTC_SRVC_RES *srvc_res = &arg->srvc_res; - param.search_res.conn_id = srvc_res->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(srvc_res->conn_id); + param.search_res.conn_id = BTC_GATT_GET_CONN_ID(srvc_res->conn_id); bta_to_btc_srvc_id(¶m.search_res.srvc_id, &srvc_res->service_uuid); - BTC_GATTC_CB_TO_APP(ESP_GATTC_SEARCH_RES_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_SEARCH_RES_EVT, gattc_if, ¶m); break; } case BTA_GATTC_READ_DESCR_EVT: { - set_read_value(¶m, &arg->read); - BTC_GATTC_CB_TO_APP(ESP_GATTC_READ_DESCR_EVT, ¶m); + set_read_value(&gattc_if, ¶m, &arg->read); + btc_gattc_cb_to_app(ESP_GATTC_READ_DESCR_EVT, gattc_if, ¶m); break; } case BTA_GATTC_WRITE_DESCR_EVT: { tBTA_GATTC_WRITE *write = &arg->write; - param.write.conn_id = write->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(write->conn_id); + param.write.conn_id = BTC_GATT_GET_CONN_ID(write->conn_id); param.write.status = write->status; bta_to_btc_srvc_id(¶m.write.srvc_id, &write->srvc_id); bta_to_btc_gatt_id(¶m.write.char_id, &write->char_id); bta_to_btc_gatt_id(¶m.write.descr_id, &write->descr_type); - BTC_GATTC_CB_TO_APP(ESP_GATTC_WRITE_DESCR_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_WRITE_DESCR_EVT, gattc_if, ¶m); break; } case BTA_GATTC_NOTIF_EVT: { tBTA_GATTC_NOTIFY *notify = &arg->notify; - param.notify.conn_id = notify->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(notify->conn_id); + param.notify.conn_id = BTC_GATT_GET_CONN_ID(notify->conn_id); memcpy(param.notify.remote_bda, notify->bda, sizeof(esp_bd_addr_t)); bta_to_btc_srvc_id(¶m.notify.srvc_id, ¬ify->char_id.srvc_id); bta_to_btc_gatt_id(¶m.notify.char_id, ¬ify->char_id.char_id); @@ -597,57 +632,63 @@ void btc_gattc_cb_handler(btc_msg_t *msg) BTA_GATTC_SendIndConfirm(notify->conn_id, ¬ify->char_id); } - BTC_GATTC_CB_TO_APP(ESP_GATTC_NOTIFY_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_NOTIFY_EVT, gattc_if, ¶m); break; } case BTA_GATTC_OPEN_EVT: { tBTA_GATTC_OPEN *open = &arg->open; + + gattc_if = open->client_if; param.open.status = open->status; - param.open.conn_id = open->conn_id; - param.open.gatt_if = open->client_if; + param.open.conn_id = BTC_GATT_GET_CONN_ID(open->conn_id); memcpy(param.open.remote_bda, open->remote_bda, sizeof(esp_bd_addr_t)); param.open.mtu = open->mtu; - BTC_GATTC_CB_TO_APP(ESP_GATTC_OPEN_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_OPEN_EVT, gattc_if, ¶m); break; } case BTA_GATTC_CLOSE_EVT: { tBTA_GATTC_CLOSE *close = &arg->close; + + gattc_if = close->client_if; param.close.status = close->status; - param.close.conn_id = close->conn_id; - param.close.gatt_if = close->client_if; + param.close.conn_id = BTC_GATT_GET_CONN_ID(close->conn_id); memcpy(param.close.remote_bda, close->remote_bda, sizeof(esp_bd_addr_t)); param.close.reason = close->reason; - BTC_GATTC_CB_TO_APP(ESP_GATTC_CLOSE_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_CLOSE_EVT, gattc_if, ¶m); break; } case BTA_GATTC_CFG_MTU_EVT: { tBTA_GATTC_CFG_MTU *cfg_mtu = &arg->cfg_mtu; - param.cfg_mtu.conn_id = cfg_mtu->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(cfg_mtu->conn_id); + param.cfg_mtu.conn_id = BTC_GATT_GET_CONN_ID(cfg_mtu->conn_id); param.cfg_mtu.status = cfg_mtu->status; param.cfg_mtu.mtu = cfg_mtu->mtu; - BTC_GATTC_CB_TO_APP(ESP_GATTC_CFG_MTU_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_CFG_MTU_EVT, gattc_if, ¶m); break; } case BTA_GATTC_ACL_EVT: { - BTC_GATTC_CB_TO_APP(ESP_GATTC_ACL_EVT, NULL); + /* Currently, this event will never happen */ break; } case BTA_GATTC_CANCEL_OPEN_EVT: { - BTC_GATTC_CB_TO_APP(ESP_GATTC_CANCEL_OPEN_EVT, NULL); + /* Currently, this event will never happen */ break; } case BTA_GATTC_CONGEST_EVT: { tBTA_GATTC_CONGEST *congest = &arg->congest; - param.congest.conn_id = congest->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(congest->conn_id); + param.congest.conn_id = BTC_GATT_GET_CONN_ID(congest->conn_id); param.congest.congested = (congest->congested == TRUE) ? true : false; - BTC_GATTC_CB_TO_APP(ESP_GATTC_CONGEST_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_CONGEST_EVT, gattc_if, ¶m); break; } case BTA_GATTC_SRVC_CHG_EVT: { memcpy(param.srvc_chg.remote_bda, arg->remote_bda, sizeof(esp_bd_addr_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_SRVC_CHG_EVT, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_SRVC_CHG_EVT, ESP_GATT_IF_NONE, ¶m); break; } default: diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c index 8ef1e9aefd..2ba3ec0f14 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -20,14 +20,52 @@ #include "btc_manage.h" #include "btc_gatts.h" #include "btc_gatt_util.h" - +#include "future.h" +#include "btc_main.h" #include "esp_gatts_api.h" -#define BTC_GATTS_CB_TO_APP(_event, _param) ((esp_profile_cb_t)btc_profile_cb_get(BTC_PID_GATTS))(_event, _param) - #define A2C_GATTS_EVT(_bta_event) (_bta_event) //BTA TO BTC EVT #define C2A_GATTS_EVT(_btc_event) (_btc_event) //BTC TO BTA EVT +typedef struct { + future_t *complete_future; + uint16_t svc_start_hdl; + esp_bt_uuid_t svc_uuid; + bool is_tab_creat_svc; + uint8_t num_handle; + uint8_t handle_idx; + uint16_t handles[ESP_GATT_ATTR_HANDLE_MAX]; +} esp_btc_creat_tab_t; + +static esp_btc_creat_tab_t btc_creat_tab_env; + +static inline void btc_gatts_cb_to_app(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + esp_gatts_cb_t btc_gatts_cb = (esp_gatts_cb_t)btc_profile_cb_get(BTC_PID_GATTS); + if (btc_gatts_cb) { + btc_gatts_cb(event, gatts_if, param); + } +} + +static inline void btc_gatts_uuid_format_convert(esp_bt_uuid_t* dest_uuid, uint16_t src_uuid_len, uint8_t* src_uuid_p) +{ + dest_uuid->len = src_uuid_len; + if(src_uuid_len == ESP_UUID_LEN_16){ + dest_uuid->uuid.uuid16 = src_uuid_p[0] + (src_uuid_p[1]<<8); + } + else if(src_uuid_len == ESP_UUID_LEN_32){ + dest_uuid->uuid.uuid32 = src_uuid_p[0] + (src_uuid_p[1]<<8) + (src_uuid_p[2]<<16) + (src_uuid_p[3]<<24); + } + else if(src_uuid_len == ESP_UUID_LEN_128){ + memcpy(dest_uuid->uuid.uuid128, src_uuid_p, src_uuid_len); + } + else{ + LOG_ERROR("%s wrong uuid length %d\n", __func__, src_uuid_len); + } + +} + + void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) { btc_ble_gatts_args_t *dst = (btc_ble_gatts_args_t *) p_dest; @@ -53,6 +91,56 @@ void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) } } break; + + } + case BTC_GATTS_ACT_ADD_CHAR:{ + if (src->add_char.char_val.attr_value != NULL){ + dst->add_char.char_val.attr_value = (uint8_t *)GKI_getbuf(src->add_char.char_val.attr_len); + if(dst->add_char.char_val.attr_value != NULL){ + memcpy(dst->add_char.char_val.attr_value, src->add_char.char_val.attr_value, + src->add_char.char_val.attr_len); + }else{ + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + } + break; + } + case BTC_GATTS_ACT_ADD_CHAR_DESCR:{ + if(src->add_descr.descr_val.attr_value != NULL){ + dst->add_descr.descr_val.attr_value = (uint8_t *)GKI_getbuf(src->add_descr.descr_val.attr_len); + if(dst->add_descr.descr_val.attr_value != NULL){ + memcpy(dst->add_descr.descr_val.attr_value, src->add_descr.descr_val.attr_value, + src->add_descr.descr_val.attr_len); + }else{ + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + } + break; + } + case BTC_GATTS_ACT_CREATE_ATTR_TAB:{ + uint8_t num_attr = src->create_attr_tab.max_nb_attr; + if(src->create_attr_tab.gatts_attr_db != NULL){ + dst->create_attr_tab.gatts_attr_db = (esp_gatts_attr_db_t *)GKI_getbuf(sizeof(esp_gatts_attr_db_t)*num_attr); + if(dst->create_attr_tab.gatts_attr_db != NULL){ + memcpy(dst->create_attr_tab.gatts_attr_db, src->create_attr_tab.gatts_attr_db, + sizeof(esp_gatts_attr_db_t)*num_attr); + }else{ + LOG_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } + case BTC_GATTS_ACT_SET_ATTR_VALUE:{ + uint8_t len = src->set_attr_val.length; + if(src->set_attr_val.value){ + dst->set_attr_val.value = (uint8_t *)GKI_getbuf(len); + if(dst->set_attr_val.value != NULL){ + memcpy(dst->set_attr_val.value, src->set_attr_val.value, len); + }else{ + LOG_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; } default: LOG_DEBUG("%s Unhandled deep copy %d\n", __func__, msg->act); @@ -85,6 +173,175 @@ void btc_gatts_arg_deep_free(btc_msg_t *msg) } +static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, + esp_gatt_if_t gatts_if, + uint8_t max_nb_attr, + uint8_t srvc_inst_id) +{ + uint16_t uuid = 0; + future_t *future_p; + esp_ble_gatts_cb_param_t param; + + //set the attribute table create service flag to ture + btc_creat_tab_env.is_tab_creat_svc = true; + btc_creat_tab_env.num_handle = max_nb_attr; + for(int i = 0; i < max_nb_attr; i++){ + if(gatts_attr_db[i].att_desc.uuid_length== ESP_UUID_LEN_16){ + uuid = (gatts_attr_db[i].att_desc.uuid_p[1] << 8) + (gatts_attr_db[i].att_desc.uuid_p[0]); + } + future_p = future_new(); + if (future_p == NULL) { + LOG_ERROR("%s failed:no mem\n", __func__); + return ; + } + btc_creat_tab_env.complete_future = future_p; + btc_creat_tab_env.handle_idx = i; + switch(uuid) + { + case ESP_GATT_UUID_PRI_SERVICE:{ + tBTA_GATT_SRVC_ID srvc_id; + esp_gatt_srvc_id_t esp_srvc_id; + + esp_srvc_id.id.inst_id = srvc_inst_id; + btc_gatts_uuid_format_convert(&esp_srvc_id.id.uuid,gatts_attr_db[i].att_desc.length, + gatts_attr_db[i].att_desc.value); + + btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); + BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, + srvc_inst_id, max_nb_attr, true); + + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + break; + } + case ESP_GATT_UUID_SEC_SERVICE:{ + tBTA_GATT_SRVC_ID srvc_id; + esp_gatt_srvc_id_t esp_srvc_id; + + esp_srvc_id.id.inst_id = srvc_inst_id; + btc_gatts_uuid_format_convert(&esp_srvc_id.id.uuid,gatts_attr_db[i].att_desc.uuid_length, + gatts_attr_db[i].att_desc.uuid_p); + btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); + BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, + srvc_inst_id, max_nb_attr, false); + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + break; + } + case ESP_GATT_UUID_INCLUDE_SERVICE:{ + esp_gatts_incl_svc_desc_t *incl_svc_desc = (esp_gatts_incl_svc_desc_t *)gatts_attr_db[i].att_desc.value; + + if(incl_svc_desc!= NULL){ + if(btc_creat_tab_env.svc_start_hdl != 0){ + BTA_GATTS_AddIncludeService(btc_creat_tab_env.svc_start_hdl, + incl_svc_desc->start_hdl); + + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + } + } + break; + } + case ESP_GATT_UUID_CHAR_DECLARE:{ + uint16_t svc_hal = 0; + tBT_UUID bta_char_uuid; + tGATT_ATTR_VAL attr_val; + esp_bt_uuid_t uuid_temp; + tBTA_GATT_PERM perm; + tBTA_GATTS_ATTR_CONTROL control; + uint8_t char_property; + + if(btc_creat_tab_env.svc_start_hdl != 0){ + svc_hal = btc_creat_tab_env.svc_start_hdl; + if((gatts_attr_db[i].att_desc.value) == NULL){ + LOG_ERROR("%s Characteristic declaration should not be NULL\n", __func__); + } + else{ + char_property = (uint8_t)(*(uint8_t*)(gatts_attr_db[i].att_desc.value)); + perm = gatts_attr_db[i+1].att_desc.perm; + attr_val.attr_len = gatts_attr_db[i+1].att_desc.length; + attr_val.attr_max_len = gatts_attr_db[i+1].att_desc.max_length; + btc_gatts_uuid_format_convert(&uuid_temp, gatts_attr_db[i+1].att_desc.uuid_length,gatts_attr_db[i+1].att_desc.uuid_p); + btc_to_bta_uuid(&bta_char_uuid, &uuid_temp); + attr_val.attr_val = gatts_attr_db[i+1].att_desc.value; + control.auto_rsp = gatts_attr_db[i+1].attr_control.auto_rsp; + BTA_GATTS_AddCharacteristic (svc_hal, &bta_char_uuid, + perm, char_property, &attr_val, &control); + + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + } + } + + break; + } + case ESP_GATT_UUID_CHAR_EXT_PROP: + case ESP_GATT_UUID_CHAR_DESCRIPTION: + case ESP_GATT_UUID_CHAR_CLIENT_CONFIG: + case ESP_GATT_UUID_CHAR_SRVR_CONFIG: + case ESP_GATT_UUID_CHAR_PRESENT_FORMAT: + case ESP_GATT_UUID_CHAR_AGG_FORMAT: + case ESP_GATT_UUID_CHAR_VALID_RANGE: + case ESP_GATT_UUID_EXT_RPT_REF_DESCR: + case ESP_GATT_UUID_RPT_REF_DESCR:{ + uint16_t svc_hal = btc_creat_tab_env.svc_start_hdl; + tBT_UUID bta_char_uuid; + esp_bt_uuid_t uuid_temp; + tGATT_ATTR_VAL attr_val; + tBTA_GATT_PERM perm = gatts_attr_db[i].att_desc.perm; + tBTA_GATTS_ATTR_CONTROL control; + + if(svc_hal != 0){ + attr_val.attr_len = gatts_attr_db[i].att_desc.length; + attr_val.attr_max_len = gatts_attr_db[i].att_desc.max_length; + attr_val.attr_val = gatts_attr_db[i].att_desc.value; + btc_gatts_uuid_format_convert(&uuid_temp, gatts_attr_db[i].att_desc.uuid_length, + gatts_attr_db[i].att_desc.uuid_p); + btc_to_bta_uuid(&bta_char_uuid, &uuid_temp); + control.auto_rsp = gatts_attr_db[i].attr_control.auto_rsp; + BTA_GATTS_AddCharDescriptor(svc_hal, perm, &bta_char_uuid, &attr_val, &control); + + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + } + break; + } + default: + break; + } + + + } + + param.add_attr_tab.status = ESP_GATT_OK; + param.add_attr_tab.num_handle = max_nb_attr; + param.add_attr_tab.handles = btc_creat_tab_env.handles; + memcpy(¶m.add_attr_tab.svc_uuid, &btc_creat_tab_env.svc_uuid, sizeof(esp_bt_uuid_t)); + + btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); + //reset the env after sent the data to app + memset(&btc_creat_tab_env, 0, sizeof(esp_btc_creat_tab_t)); + + //release the flag vaule to false after finish the service created. + btc_creat_tab_env.is_tab_creat_svc = false; +} + +void btc_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, uint8_t **value) +{ + + BTA_GetAttributeValue(attr_handle, length, value); +} + static void btc_gatts_cb_param_copy_req(btc_msg_t *msg, void *p_dest, void *p_src) { @@ -131,7 +388,6 @@ static void btc_gatts_cb_param_copy_free(btc_msg_t *msg, tBTA_GATTS *p_data) GKI_freebuf(p_data->req_data.p_data); } break; - default: break; } @@ -142,11 +398,43 @@ static void btc_gatts_inter_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) { bt_status_t status; btc_msg_t msg; - + msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_GATTS; msg.act = event; + if(btc_creat_tab_env.is_tab_creat_svc && btc_creat_tab_env.complete_future){ + switch(event){ + case BTA_GATTS_CREATE_EVT:{ + //save the service handle to the btc module after used + //the attribute table method to creat a service + bta_to_btc_uuid(&btc_creat_tab_env.svc_uuid, &p_data->create.uuid); + uint8_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.svc_start_hdl = p_data->create.service_id; + btc_creat_tab_env.handles[index] = p_data->create.service_id; + break; + } + case BTA_GATTS_ADD_INCL_SRVC_EVT:{ + uint8_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.handles[index] = p_data->add_result.attr_id; + break; + } + case BTA_GATTS_ADD_CHAR_EVT:{ + uint8_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.handles[index] = p_data->add_result.attr_id - 1; + btc_creat_tab_env.handles[index+1] = p_data->add_result.attr_id; + break; + } + case BTA_GATTS_ADD_CHAR_DESCR_EVT:{ + uint8_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.handles[index] = p_data->add_result.attr_id; + break; + } + default: + break; + } + future_ready(btc_creat_tab_env.complete_future, FUTURE_SUCCESS); + } status = btc_transfer_context(&msg, p_data, sizeof(tBTA_GATTS), btc_gatts_cb_param_copy_req); @@ -171,16 +459,22 @@ void btc_gatts_call_handler(btc_msg_t *msg) break; } case BTC_GATTS_ACT_APP_UNREGISTER: - BTA_GATTS_AppDeregister(arg->app_unreg.gatt_if); + BTA_GATTS_AppDeregister(arg->app_unreg.gatts_if); break; case BTC_GATTS_ACT_CREATE_SERVICE: { tBTA_GATT_SRVC_ID srvc_id; btc_to_bta_srvc_id(&srvc_id, &arg->create_srvc.service_id); - BTA_GATTS_CreateService(arg->create_srvc.gatt_if, &srvc_id.id.uuid, + BTA_GATTS_CreateService(arg->create_srvc.gatts_if, &srvc_id.id.uuid, srvc_id.id.inst_id, arg->create_srvc.num_handle, srvc_id.is_primary); break; } + case BTC_GATTS_ACT_CREATE_ATTR_TAB: + btc_gatts_act_create_attr_tab(arg->create_attr_tab.gatts_attr_db, + arg->create_attr_tab.gatts_if, + arg->create_attr_tab.max_nb_attr, + arg->create_attr_tab.srvc_inst_id); + break; case BTC_GATTS_ACT_DELETE_SERVICE: BTA_GATTS_DeleteService(arg->delete_srvc.service_handle); break; @@ -198,13 +492,17 @@ void btc_gatts_call_handler(btc_msg_t *msg) btc_to_bta_uuid(&uuid, &arg->add_char.char_uuid); BTA_GATTS_AddCharacteristic(arg->add_char.service_handle, &uuid, - arg->add_char.perm, arg->add_char.property); + arg->add_char.perm, arg->add_char.property, + (tGATT_ATTR_VAL *)&arg->add_char.char_val, + (tBTA_GATTS_ATTR_CONTROL *)&arg->add_char.attr_control); break; } case BTC_GATTS_ACT_ADD_CHAR_DESCR: { tBT_UUID uuid; btc_to_bta_uuid(&uuid, &arg->add_descr.descr_uuid); - BTA_GATTS_AddCharDescriptor(arg->add_descr.service_handle, arg->add_descr.perm, &uuid); + BTA_GATTS_AddCharDescriptor(arg->add_descr.service_handle, arg->add_descr.perm, &uuid, + (tBTA_GATT_ATTR_VAL *)&arg->add_descr.descr_val, + (tBTA_GATTS_ATTR_CONTROL *)&arg->add_descr.attr_control); break; } case BTC_GATTS_ACT_SEND_INDICATE: @@ -227,9 +525,12 @@ void btc_gatts_call_handler(btc_msg_t *msg) } param.rsp.status = 0; - BTC_GATTS_CB_TO_APP(ESP_GATTS_RESPONSE_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_RESPONSE_EVT, BTC_GATT_GET_GATT_IF(arg->send_rsp.conn_id), ¶m); break; } + case BTC_GATTS_ACT_SET_ATTR_VALUE: + + break; case BTC_GATTS_ACT_OPEN: { // Ensure device is in inquiry database tBTA_GATT_TRANSPORT transport = BTA_GATT_TRANSPORT_LE; @@ -252,7 +553,7 @@ void btc_gatts_call_handler(btc_msg_t *msg) transport = BTA_GATT_TRANSPORT_LE; // Connect! - BTA_GATTS_Open(arg->open.gatt_if, arg->open.remote_bda, + BTA_GATTS_Open(arg->open.gatts_if, arg->open.remote_bda, arg->open.is_direct, transport); break; } @@ -277,34 +578,39 @@ void btc_gatts_call_handler(btc_msg_t *msg) void btc_gatts_cb_handler(btc_msg_t *msg) { esp_ble_gatts_cb_param_t param; - tBTA_GATTS *p_data = (tBTA_GATTS *)msg->arg; + esp_gatt_if_t gatts_if; switch (msg->act) { case BTA_GATTS_REG_EVT: { + gatts_if = p_data->reg_oper.server_if; param.reg.status = p_data->reg_oper.status; - param.reg.gatt_if = p_data->reg_oper.server_if; param.reg.app_id = p_data->reg_oper.uuid.uu.uuid16; - BTC_GATTS_CB_TO_APP(ESP_GATTS_REG_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_REG_EVT, gatts_if, ¶m); break; } - case BTA_GATTS_DEREG_EVT: - // do nothing + case BTA_GATTS_DEREG_EVT: { + gatts_if = p_data->reg_oper.server_if; + btc_gatts_cb_to_app(ESP_GATTS_UNREG_EVT, gatts_if, NULL); break; + } case BTA_GATTS_READ_EVT: { - param.read.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.read.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.read.trans_id = p_data->req_data.trans_id; memcpy(param.read.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); - param.read.handle = p_data->req_data.p_data->read_req.handle, - param.read.offset = p_data->req_data.p_data->read_req.offset, - param.read.is_long = p_data->req_data.p_data->read_req.is_long, + param.read.handle = p_data->req_data.p_data->read_req.handle; + param.read.offset = p_data->req_data.p_data->read_req.offset; + param.read.is_long = p_data->req_data.p_data->read_req.is_long; - BTC_GATTS_CB_TO_APP(ESP_GATTS_READ_EVT, ¶m); + param.read.need_rsp = p_data->req_data.p_data->read_req.need_rsp; + btc_gatts_cb_to_app(ESP_GATTS_READ_EVT, gatts_if, ¶m); break; } case BTA_GATTS_WRITE_EVT: { - param.write.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.write.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.write.trans_id = p_data->req_data.trans_id; memcpy(param.write.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); param.write.handle = p_data->req_data.p_data->write_req.handle; @@ -314,116 +620,128 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.write.len = p_data->req_data.p_data->write_req.len; param.write.value = p_data->req_data.p_data->write_req.value; - BTC_GATTS_CB_TO_APP(ESP_GATTS_WRITE_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_WRITE_EVT, gatts_if, ¶m); break; } case BTA_GATTS_EXEC_WRITE_EVT: { - param.exec_write.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.exec_write.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.exec_write.trans_id = p_data->req_data.trans_id; memcpy(param.exec_write.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); param.exec_write.exec_write_flag = p_data->req_data.p_data->exec_write; - BTC_GATTS_CB_TO_APP(ESP_GATTS_EXEC_WRITE_EVT, ¶m); - + btc_gatts_cb_to_app(ESP_GATTS_EXEC_WRITE_EVT, gatts_if, ¶m); break; } case BTA_GATTS_MTU_EVT: - param.mtu.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.mtu.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.mtu.mtu = p_data->req_data.p_data->mtu; - BTC_GATTS_CB_TO_APP(ESP_GATTS_MTU_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_MTU_EVT, gatts_if, ¶m); break; case BTA_GATTS_CONF_EVT: - param.conf.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.conf.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.conf.status = p_data->req_data.status; - BTC_GATTS_CB_TO_APP(ESP_GATTS_CONF_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_CONF_EVT, gatts_if, ¶m); break; case BTA_GATTS_CREATE_EVT: + gatts_if = p_data->create.server_if; param.create.status = p_data->create.status; - param.create.gatt_if = p_data->create.server_if; param.create.service_handle = p_data->create.service_id; param.create.service_id.is_primary = p_data->create.is_primary; param.create.service_id.id.inst_id = p_data->create.svc_instance; bta_to_btc_uuid(¶m.create.service_id.id.uuid, &p_data->create.uuid); - BTC_GATTS_CB_TO_APP(ESP_GATTS_CREATE_EVT, ¶m); + + btc_gatts_cb_to_app(ESP_GATTS_CREATE_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_INCL_SRVC_EVT: + gatts_if = p_data->add_result.server_if; param.add_incl_srvc.status = p_data->add_result.status; - param.add_incl_srvc.gatt_if = p_data->add_result.server_if; param.add_incl_srvc.attr_handle = p_data->add_result.attr_id; param.add_incl_srvc.service_handle = p_data->add_result.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_INCL_SRVC_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_ADD_INCL_SRVC_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_CHAR_EVT: + gatts_if = p_data->add_result.server_if; param.add_char.status = p_data->add_result.status; - param.add_char.gatt_if = p_data->add_result.server_if; param.add_char.attr_handle = p_data->add_result.attr_id; param.add_char.service_handle = p_data->add_result.service_id; bta_to_btc_uuid(¶m.add_char.char_uuid, &p_data->add_result.char_uuid); - BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_CHAR_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_ADD_CHAR_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_CHAR_DESCR_EVT: + gatts_if = p_data->add_result.server_if; param.add_char_descr.status = p_data->add_result.status; - param.add_char_descr.gatt_if = p_data->add_result.server_if; param.add_char_descr.attr_handle = p_data->add_result.attr_id; param.add_char_descr.service_handle = p_data->add_result.service_id; bta_to_btc_uuid(¶m.add_char_descr.char_uuid, &p_data->add_result.char_uuid); - BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_CHAR_DESCR_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_ADD_CHAR_DESCR_EVT, gatts_if, ¶m); break; case BTA_GATTS_DELELTE_EVT: + gatts_if = p_data->srvc_oper.server_if; param.del.status = p_data->srvc_oper.status; - param.del.gatt_if = p_data->srvc_oper.server_if; param.del.service_handle = p_data->srvc_oper.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_DELETE_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_DELETE_EVT, gatts_if, ¶m); break; case BTA_GATTS_START_EVT: + gatts_if = p_data->srvc_oper.server_if; param.start.status = p_data->srvc_oper.status; - param.start.gatt_if = p_data->srvc_oper.server_if; param.start.service_handle = p_data->srvc_oper.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_START_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_START_EVT, gatts_if, ¶m); break; case BTA_GATTS_STOP_EVT: + gatts_if = p_data->srvc_oper.server_if; param.stop.status = p_data->srvc_oper.status; - param.stop.gatt_if = p_data->srvc_oper.server_if; param.stop.service_handle = p_data->srvc_oper.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_STOP_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_STOP_EVT, gatts_if, ¶m); break; case BTA_GATTS_CONNECT_EVT: - param.connect.conn_id = p_data->conn.conn_id; - param.connect.gatt_if = p_data->conn.server_if; + gatts_if = p_data->conn.server_if; + param.connect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id); param.connect.is_connected = true; memcpy(param.connect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); - BTC_GATTS_CB_TO_APP(ESP_GATTS_CONNECT_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_CONNECT_EVT, gatts_if, ¶m); break; case BTA_GATTS_DISCONNECT_EVT: - param.connect.conn_id = p_data->conn.conn_id; - param.connect.gatt_if = p_data->conn.server_if; - param.connect.is_connected = false; - memcpy(param.connect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); + gatts_if = p_data->conn.server_if; + param.disconnect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id); + param.disconnect.is_connected = false; + memcpy(param.disconnect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); - BTC_GATTS_CB_TO_APP(ESP_GATTS_DISCONNECT_EVT, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_DISCONNECT_EVT, gatts_if, ¶m); break; case BTA_GATTS_OPEN_EVT: - // do nothing + // do nothing case BTA_GATTS_CANCEL_OPEN_EVT: - // do nothing + // do nothing case BTA_GATTS_CLOSE_EVT: - // do nothing + // do nothing case BTA_GATTS_LISTEN_EVT: // do nothing break; case BTA_GATTS_CONGEST_EVT: - param.congest.conn_id = p_data->congest.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->congest.conn_id); + param.congest.conn_id = BTC_GATT_GET_CONN_ID(p_data->congest.conn_id); param.congest.congested = p_data->congest.congested; + btc_gatts_cb_to_app(ESP_GATTS_CONGEST_EVT, gatts_if, ¶m); + break; + case BTA_GATTS_SET_ATTR_VAL_EVT: + gatts_if = p_data->attr_val.server_if; + param.set_attr_val.srvc_handle = p_data->attr_val.service_id; + param.set_attr_val.attr_handle = p_data->attr_val.attr_id; + param.set_attr_val.status = p_data->attr_val.status; + btc_gatts_cb_to_app(ESP_GATTS_SET_ATTR_VAL_EVT, gatts_if, ¶m); break; default: // do nothing diff --git a/components/bt/bluedroid/btc/profile/std/gatt/include/btc_gatt_util.h b/components/bt/bluedroid/btc/profile/std/include/btc_gatt_util.h similarity index 80% rename from components/bt/bluedroid/btc/profile/std/gatt/include/btc_gatt_util.h rename to components/bt/bluedroid/btc/profile/std/include/btc_gatt_util.h index 540e118d59..99083f74f9 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/include/btc_gatt_util.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gatt_util.h @@ -21,6 +21,10 @@ #include "esp_gatt_defs.h" #include "esp_gattc_api.h" +#define BTC_GATT_CREATE_CONN_ID(gatt_if, conn_id) ((uint16_t) ((((uint8_t)(conn_id)) << 8) | ((uint8_t)(gatt_if)))) +#define BTC_GATT_GET_CONN_ID(conn_id) (((uint16_t)(conn_id)) >> 8) +#define BTC_GATT_GET_GATT_IF(conn_id) ((uint8_t)(conn_id)) + void btc128_to_bta_uuid(tBT_UUID *p_dest, uint8_t *p_src); void btc_to_bta_uuid(tBT_UUID *p_dest, esp_bt_uuid_t *p_src); void btc_to_bta_gatt_id(tBTA_GATT_ID *p_dest, esp_gatt_id_t *p_src); @@ -31,6 +35,6 @@ void bta_to_btc_uuid(esp_bt_uuid_t *p_dest, tBT_UUID *p_src); void bta_to_btc_gatt_id(esp_gatt_id_t *p_dest, tBTA_GATT_ID *p_src); void bta_to_btc_srvc_id(esp_gatt_srvc_id_t *p_dest, tBTA_GATT_SRVC_ID *p_src); -uint16_t set_read_value(esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src); +uint16_t set_read_value(uint8_t *gattc_if, esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src); #endif /* __BTC_GATT_UTIL_H__*/ diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_gattc.h b/components/bt/bluedroid/btc/profile/std/include/btc_gattc.h index 4bca4ae5ab..aef8418957 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_gattc.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gattc.h @@ -51,11 +51,11 @@ typedef union { } app_reg; //BTC_GATTC_ACT_APP_UNREGISTER, struct app_unreg_arg { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gattc_if; } app_unreg; //BTC_GATTC_ACT_OPEN, struct open_arg { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gattc_if; esp_bd_addr_t remote_bda; bool is_direct; } open; @@ -162,14 +162,14 @@ typedef union { } exec_write; //BTC_GATTC_ACT_REG_FOR_NOTIFY, struct reg_for_notify_arg { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gattc_if; esp_bd_addr_t remote_bda; esp_gatt_srvc_id_t service_id; esp_gatt_id_t char_id; } reg_for_notify; //BTC_GATTC_ACT_UNREG_FOR_NOTIFY struct unreg_for_notify_arg { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gattc_if; esp_bd_addr_t remote_bda; esp_gatt_srvc_id_t service_id; esp_gatt_id_t char_id; diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h b/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h index 0ba0f8869a..caae44de44 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h @@ -24,6 +24,7 @@ typedef enum { BTC_GATTS_ACT_APP_REGISTER = 0, BTC_GATTS_ACT_APP_UNREGISTER, BTC_GATTS_ACT_CREATE_SERVICE, + BTC_GATTS_ACT_CREATE_ATTR_TAB, BTC_GATTS_ACT_DELETE_SERVICE, BTC_GATTS_ACT_START_SERVICE, BTC_GATTS_ACT_STOP_SERVICE, @@ -32,6 +33,7 @@ typedef enum { BTC_GATTS_ACT_ADD_CHAR_DESCR, BTC_GATTS_ACT_SEND_INDICATE, BTC_GATTS_ACT_SEND_RESPONSE, + BTC_GATTS_ACT_SET_ATTR_VALUE, BTC_GATTS_ACT_OPEN, BTC_GATTS_ACT_CLOSE, } btc_gatts_act_t; @@ -42,46 +44,67 @@ typedef union { struct app_reg_args { uint16_t app_id; } app_reg; + //BTC_GATTS_ACT_APP_UNREGISTER, struct app_unreg_args { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gatts_if; } app_unreg; + //BTC_GATTS_ACT_CREATE_SERVICE, struct create_srvc_args { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gatts_if; esp_gatt_srvc_id_t service_id; uint16_t num_handle; } create_srvc; + + //BTC_GATTS_ACT_CREATE_ATTR_TAB + struct create_attr_tab_args{ + esp_gatt_if_t gatts_if; + uint8_t srvc_inst_id; + uint8_t max_nb_attr; + esp_gatts_attr_db_t *gatts_attr_db; + }create_attr_tab; + //BTC_GATTS_ACT_DELETE_SERVICE, struct delete_srvc_args { uint16_t service_handle; } delete_srvc; + //BTC_GATTS_ACT_START_SERVICE, struct start_srvc_args { uint16_t service_handle; } start_srvc; + //BTC_GATTS_ACT_STOP_SERVICE, struct stop_srvc_args { uint16_t service_handle; } stop_srvc; + //BTC_GATTS_ACT_ADD_INCLUDE_SERVICE, struct add_incl_srvc_args { uint16_t service_handle; uint16_t included_service_handle; } add_incl_srvc; + //BTC_GATTS_ACT_ADD_CHAR, struct add_char_args { uint16_t service_handle; esp_bt_uuid_t char_uuid; esp_gatt_perm_t perm; esp_gatt_char_prop_t property; + esp_attr_control_t attr_control; + esp_attr_value_t char_val; } add_char; + //BTC_GATTS_ACT_ADD_CHAR_DESCR, struct add_descr_args { - uint16_t service_handle; + uint16_t service_handle; esp_bt_uuid_t descr_uuid; esp_gatt_perm_t perm; + esp_attr_control_t attr_control; + esp_attr_value_t descr_val; } add_descr; + //BTC_GATTS_ACT_SEND_INDICATE, struct send_indicate_args { uint16_t conn_id; @@ -90,6 +113,7 @@ typedef union { uint16_t value_len; uint8_t *value; } send_ind; + //BTC_GATTS_ACT_SEND_RESPONSE, struct send_rsp_args { uint16_t conn_id; @@ -97,21 +121,32 @@ typedef union { esp_gatt_status_t status; esp_gatt_rsp_t *rsp; } send_rsp; + + //BTC_GATTS_SET_ATTR_VALUE + struct set_attr_val_args{ + uint16_t length; + uint8_t *value; + } set_attr_val; + //BTC_GATTS_ACT_OPEN, struct open_args { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gatts_if; esp_bd_addr_t remote_bda; bool is_direct; } open; + //BTC_GATTS_ACT_CLOSE, struct close_args { uint16_t conn_id; } close; + } btc_ble_gatts_args_t; void btc_gatts_call_handler(btc_msg_t *msg); void btc_gatts_cb_handler(btc_msg_t *msg); void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, uint8_t **value); + #endif /* __BTC_GATTS_H__ */ diff --git a/components/bt/bluedroid/btif/btif_avk.c b/components/bt/bluedroid/btif/btif_avk.c index d09e261ba7..06315f7e32 100644 --- a/components/bt/bluedroid/btif/btif_avk.c +++ b/components/bt/bluedroid/btif/btif_avk.c @@ -92,7 +92,7 @@ typedef struct { ** Static variables ******************************************************************************/ -static esp_profile_cb_t bt_av_sink_callback = NULL; +static esp_a2d_cb_t bt_av_sink_callback = NULL; static btif_av_cb_t btif_av_cb = {0}; static TIMER_LIST_ENT tle_av_open_on_rc; @@ -952,7 +952,7 @@ bt_status_t btif_av_init() * Returns bt_status_t * */ -esp_err_t esp_a2d_register_callback(esp_profile_cb_t callback) +esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback) { // TODO: need protection against race bt_av_sink_callback = callback; diff --git a/components/bt/bluedroid/btif/btif_core.c b/components/bt/bluedroid/btif/btif_core.c index b2ff5ccd6c..f9d3a05739 100644 --- a/components/bt/bluedroid/btif/btif_core.c +++ b/components/bt/bluedroid/btif/btif_core.c @@ -314,7 +314,7 @@ bt_status_t btif_init_bluetooth(void) goto error_exit; } xBtifQueue = xQueueCreate(60, sizeof(void *)); - xTaskCreate(btif_task_thread_handler, "BtifT", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifTaskHandle); + xTaskCreatePinnedToCore(btif_task_thread_handler, "BtifT", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifTaskHandle, 0); fixed_queue_register_dequeue(btif_msg_queue, bt_jni_msg_ready); return BT_STATUS_SUCCESS; diff --git a/components/bt/bluedroid/btif/btif_media_task.c b/components/bt/bluedroid/btif/btif_media_task.c index 992a324b10..87189b250d 100644 --- a/components/bt/bluedroid/btif/btif_media_task.c +++ b/components/bt/bluedroid/btif/btif_media_task.c @@ -421,7 +421,7 @@ bool btif_a2dp_start_media_task(void) if (xBtifMediaQueue == 0) { goto error_exit; } - xTaskCreate(btif_media_task_handler, "BtifMediaT\n", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifMediaTaskHandle); + xTaskCreatePinnedToCore(btif_media_task_handler, "BtifMediaT\n", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifMediaTaskHandle, 0); if (xBtifMediaTaskHandle == NULL) { goto error_exit; } diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index eb61903578..5d4824d9b0 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -57,7 +57,7 @@ typedef struct { static hci_hal_env_t hci_hal_env; static const hci_hal_t interface; static const hci_hal_callbacks_t *callbacks; -static const vhci_host_callback_t vhci_host_cb; +static const esp_vhci_host_callback_t vhci_host_cb; static xTaskHandle xHciH4TaskHandle; static xQueueHandle xHciH4Queue; @@ -102,10 +102,10 @@ static bool hal_open(const hci_hal_callbacks_t *upper_callbacks) hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, SIZE_MAX); xHciH4Queue = xQueueCreate(HCI_H4_QUEUE_NUM, sizeof(BtTaskEvt_t)); - xTaskCreate(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle); + xTaskCreatePinnedToCore(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle, 0); //register vhci host cb - API_vhci_host_register_callback(&vhci_host_cb); + esp_vhci_host_register_callback(&vhci_host_cb); return true; } @@ -147,7 +147,7 @@ static uint16_t transmit_data(serial_data_type_t type, BTTRC_DUMP_BUFFER("Transmit Pkt", data, length); // TX Data to target - API_vhci_host_send_packet(data, length); + esp_vhci_host_send_packet(data, length); // Be nice and restore the old value of that byte *(data) = previous_byte; @@ -175,7 +175,7 @@ void hci_hal_h4_task_post(void) evt.sig = 0xff; evt.par = 0; - if (xQueueSend(xHciH4Queue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xHciH4Queue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("xHciH4Queue failed\n"); } } @@ -273,7 +273,7 @@ static int host_recv_pkt_cb(uint8_t *data, uint16_t len) return 0; } -static const vhci_host_callback_t vhci_host_cb = { +static const esp_vhci_host_callback_t vhci_host_cb = { .notify_host_send_available = host_send_pkt_available_cb, .notify_host_recv = host_recv_pkt_cb, }; diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index cafd794942..f39c06caed 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -109,7 +109,7 @@ int hci_start_up(void) } xHciHostQueue = xQueueCreate(HCI_HOST_QUEUE_NUM, sizeof(BtTaskEvt_t)); - xTaskCreate(hci_host_thread_handler, HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, NULL, HCI_HOST_TASK_PRIO, &xHciHostTaskHandle); + xTaskCreatePinnedToCore(hci_host_thread_handler, HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, NULL, HCI_HOST_TASK_PRIO, &xHciHostTaskHandle, 0); packet_fragmenter->init(&packet_fragmenter_callbacks); hal->open(&hal_callbacks); @@ -146,7 +146,7 @@ void hci_host_task_post(void) evt.sig = 0xff; evt.par = 0; - if (xQueueSend(xHciHostQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xHciHostQueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("xHciHostQueue failed\n"); } } @@ -226,7 +226,7 @@ static void hci_host_thread_handler(void *arg) for (;;) { if (pdTRUE == xQueueReceive(xHciHostQueue, &e, (portTickType)portMAX_DELAY)) { if (e.sig == 0xff) { - if (API_vhci_host_check_send_available()) { + if (esp_vhci_host_check_send_available()) { /*Now Target only allowed one packet per TX*/ BT_HDR *pkt = packet_fragmenter->fragment_current_packet(); if (pkt != NULL) { diff --git a/components/bt/bluedroid/include/bt_trace.h b/components/bt/bluedroid/include/bt_trace.h index fd92f6e8c5..787c4c8aec 100644 --- a/components/bt/bluedroid/include/bt_trace.h +++ b/components/bt/bluedroid/include/bt_trace.h @@ -32,7 +32,7 @@ #define assert(x) do { if (!(x)) BT_PRINTF("bt host error %s %u\n", __FILE__, __LINE__); } while (0) #endif -inline void trc_dump_buffer(uint8_t *prefix, uint8_t *data, uint16_t len) +inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t len) { uint16_t i; @@ -41,14 +41,13 @@ inline void trc_dump_buffer(uint8_t *prefix, uint8_t *data, uint16_t len) } if (prefix) { - BT_PRINTF("%s:\t", prefix); + BT_PRINTF("%s: len %d\n", prefix, len); } - for (i = 0; i < len; i++) { - BT_PRINTF(" %02x", *(data + i)); - if (!((i + 1) & 0xf)) { - BT_PRINTF("\n"); - } + for (i = 0; i < len; i+=16) { + BT_PRINTF("%02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x\n", + *(data + i), *(data + i + 1), *(data + i + 2), *(data + i + 3), *(data + i + 4), *(data + i + 5), *(data + i + 6), *(data + i + 7), + *(data + i + 8), *(data + i + 9), *(data + i + 10), *(data + i + 11), *(data + i + 12), *(data + i + 13), *(data + i + 14), *(data + i + 15)); } BT_PRINTF("\n"); } diff --git a/components/bt/bluedroid/osi/osi_arch.c b/components/bt/bluedroid/osi/osi_arch.c index e896efd871..d1d0185aec 100644 --- a/components/bt/bluedroid/osi/osi_arch.c +++ b/components/bt/bluedroid/osi/osi_arch.c @@ -137,9 +137,9 @@ osi_sem_wait(osi_sem_t *sem, uint32_t timeout) StartTime = xTaskGetTickCount(); if (timeout != 0) { - if (xSemaphoreTake(*sem, timeout / portTICK_RATE_MS) == pdTRUE) { + if (xSemaphoreTake(*sem, timeout / portTICK_PERIOD_MS) == pdTRUE) { EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -153,7 +153,7 @@ osi_sem_wait(osi_sem_t *sem, uint32_t timeout) while (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE); EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -190,7 +190,7 @@ osi_now(void) void osi_delay_ms(uint32_t ms) { - vTaskDelay(ms / portTICK_RATE_MS); + vTaskDelay(ms / portTICK_PERIOD_MS); } diff --git a/components/bt/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/bluedroid/stack/btm/btm_ble_gap.c index 4ded30de83..98a8a23689 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_gap.c @@ -1089,7 +1089,6 @@ tBTM_STATUS BTM_BleSetAdvParamsStartAdv(UINT16 adv_int_min, UINT16 adv_int_max, p_cb->adv_chnl_map = chnl_map; p_addr_cb->own_addr_type = own_bda_type; p_cb->evt_type = adv_type; - p_cb->adv_mode = BTM_BLE_ADV_ENABLE; p_cb->afp = afp; if (p_dir_bda) { diff --git a/components/bt/bluedroid/stack/btu/btu_init.c b/components/bt/bluedroid/stack/btu/btu_init.c index 0f13e318a4..2ed561bcf2 100644 --- a/components/bt/bluedroid/stack/btu/btu_init.c +++ b/components/bt/bluedroid/stack/btu/btu_init.c @@ -201,7 +201,7 @@ void BTU_StartUp(void) } xBtuQueue = xQueueCreate(BTU_QUEUE_NUM, sizeof(BtTaskEvt_t)); - xTaskCreate(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle); + xTaskCreatePinnedToCore(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle, 0); btu_task_post(SIG_BTU_START_UP); /* // Continue startup on bt workqueue thread. diff --git a/components/bt/bluedroid/stack/btu/btu_task.c b/components/bt/bluedroid/stack/btu/btu_task.c index 8aaeb2492d..1123c064e8 100644 --- a/components/bt/bluedroid/stack/btu/btu_task.c +++ b/components/bt/bluedroid/stack/btu/btu_task.c @@ -338,7 +338,7 @@ void btu_task_post(uint32_t sig) evt.sig = sig; evt.par = 0; - if (xQueueSend(xBtuQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xBtuQueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("xBtuQueue failed\n"); } } diff --git a/components/bt/bluedroid/stack/gap/gap_ble.c b/components/bt/bluedroid/stack/gap/gap_ble.c index b560fad651..bf9ea08369 100644 --- a/components/bt/bluedroid/stack/gap/gap_ble.c +++ b/components/bt/bluedroid/stack/gap/gap_ble.c @@ -392,7 +392,8 @@ void gap_attr_db_init(void) */ uuid.len = LEN_UUID_16; uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_DEVICE_NAME; - p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ); + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + NULL, NULL); p_db_attr ++; /* add Icon characteristic @@ -401,7 +402,8 @@ void gap_attr_db_init(void) p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, - GATT_CHAR_PROP_BIT_READ); + GATT_CHAR_PROP_BIT_READ, + NULL, NULL); p_db_attr ++; #if ((defined BTM_PERIPHERAL_ENABLED) && (BTM_PERIPHERAL_ENABLED == TRUE)) @@ -416,7 +418,8 @@ void gap_attr_db_init(void) p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, - GATT_CHAR_PROP_BIT_READ); + GATT_CHAR_PROP_BIT_READ, + NULL, NULL); p_db_attr ++; #endif @@ -424,7 +427,8 @@ void gap_attr_db_init(void) uuid.len = LEN_UUID_16; uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_CENTRAL_ADDR_RESOL; p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, - GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ); + GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + NULL, NULL); p_db_attr->attr_value.addr_resolution = 0; p_db_attr++; diff --git a/components/bt/bluedroid/stack/gatt/gatt_api.c b/components/bt/bluedroid/stack/gatt/gatt_api.c index c464508da9..7a9d125a97 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_api.c +++ b/components/bt/bluedroid/stack/gatt/gatt_api.c @@ -151,10 +151,10 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, tBT_UUID *p_app_uuid128; - GATT_TRACE_API ("GATTS_CreateService" ); + GATT_TRACE_API ("GATTS_CreateService\n" ); if (p_reg == NULL) { - GATT_TRACE_ERROR ("Inavlid gatt_if=%d", gatt_if); + GATT_TRACE_ERROR ("Inavlid gatt_if=%d\n", gatt_if); return (0); } @@ -162,7 +162,7 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, if ((p_list = gatt_find_hdl_buffer_by_app_id(p_app_uuid128, p_svc_uuid, svc_inst)) != NULL) { s_hdl = p_list->asgn_range.s_handle; - GATT_TRACE_DEBUG ("Service already been created!!"); + GATT_TRACE_DEBUG ("Service already been created!!\n"); } else { if ( (p_svc_uuid->len == LEN_UUID_16) && (p_svc_uuid->uu.uuid16 == UUID_SERVCLASS_GATT_SERVER)) { s_hdl = gatt_cb.hdl_cfg.gatt_start_hdl; @@ -184,13 +184,13 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, /* check for space */ if (num_handles > (0xFFFF - s_hdl + 1)) { - GATT_TRACE_ERROR ("GATTS_ReserveHandles: no handles, s_hdl: %u needed: %u", s_hdl, num_handles); + GATT_TRACE_ERROR ("GATTS_ReserveHandles: no handles, s_hdl: %u needed: %u\n", s_hdl, num_handles); return (0); } if ( (p_list = gatt_alloc_hdl_buffer()) == NULL) { /* No free entry */ - GATT_TRACE_ERROR ("GATTS_ReserveHandles: no free handle blocks"); + GATT_TRACE_ERROR ("GATTS_ReserveHandles: no free handle blocks\n"); return (0); } @@ -210,7 +210,7 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, /* add a pending new service change item to the list */ if ( (p_buf = gatt_add_pending_new_srv_start(&p_list->asgn_range)) == NULL) { /* No free entry */ - GATT_TRACE_ERROR ("gatt_add_pending_new_srv_start: no free blocks"); + GATT_TRACE_ERROR ("gatt_add_pending_new_srv_start: no free blocks\n"); if (p_list) { gatt_remove_an_item_from_list(p_list_info, p_list); @@ -219,12 +219,12 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, return (0); } - GATT_TRACE_DEBUG ("Add a new srv chg item"); + GATT_TRACE_DEBUG ("Add a new srv chg item\n"); } } if (!gatts_init_service_db(&p_list->svc_db, p_svc_uuid, is_pri, s_hdl , num_handles)) { - GATT_TRACE_ERROR ("GATTS_ReserveHandles: service DB initialization failed"); + GATT_TRACE_ERROR ("GATTS_ReserveHandles: service DB initialization failed\n"); if (p_list) { gatt_remove_an_item_from_list(p_list_info, p_list); gatt_free_hdl_buffer(p_list); @@ -236,12 +236,6 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, return (0); } - GATT_TRACE_DEBUG ("GATTS_CreateService(success): handles needed:%u s_hdl=%u e_hdl=%u %s[%x] is_primary=%d", - num_handles, p_list->asgn_range.s_handle , p_list->asgn_range.e_handle, - ((p_list->asgn_range.svc_uuid.len == 2) ? "uuid16" : "uuid128" ), - p_list->asgn_range.svc_uuid.uu.uuid16, - p_list->asgn_range.is_primary); - return (s_hdl); } @@ -295,25 +289,27 @@ UINT16 GATTS_AddIncludeService (UINT16 service_handle, UINT16 include_svc_handle ** *******************************************************************************/ UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, - tGATT_PERM perm, tGATT_CHAR_PROP property) + tGATT_PERM perm, tGATT_CHAR_PROP property, + tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) { tGATT_HDL_LIST_ELEM *p_decl; if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { - GATT_TRACE_DEBUG("Service not created"); + GATT_TRACE_DEBUG("Service not created\n"); return 0; } /* data validity checking */ if ( ((property & GATT_CHAR_PROP_BIT_AUTH) && !(perm & GATT_WRITE_SIGNED_PERM)) || ((perm & GATT_WRITE_SIGNED_PERM) && !(property & GATT_CHAR_PROP_BIT_AUTH)) ) { - GATT_TRACE_DEBUG("Invalid configuration property=0x%x perm=0x%x ", property, perm); + GATT_TRACE_DEBUG("Invalid configuration property=0x%x perm=0x%x\n ", property, perm); return 0; } return gatts_add_characteristic(&p_decl->svc_db, perm, property, - p_char_uuid); + p_char_uuid, + attr_val, control); } /******************************************************************************* ** @@ -336,7 +332,7 @@ UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, *******************************************************************************/ UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, tGATT_PERM perm, - tBT_UUID *p_descr_uuid) + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) { tGATT_HDL_LIST_ELEM *p_decl; @@ -353,7 +349,8 @@ UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, return gatts_add_char_descr(&p_decl->svc_db, perm, - p_descr_uuid); + p_descr_uuid, + attr_val, control); } /******************************************************************************* @@ -493,9 +490,9 @@ tGATT_STATUS GATTS_StartService (tGATT_IF gatt_if, UINT16 service_handle, gatt_add_a_srv_to_list(&gatt_cb.srv_list_info, &gatt_cb.srv_list[i_sreg]); - GATT_TRACE_DEBUG ("allocated i_sreg=%d ", i_sreg); + GATT_TRACE_DEBUG ("allocated i_sreg=%d\n", i_sreg); - GATT_TRACE_DEBUG ("s_hdl=%d e_hdl=%d type=0x%x svc_inst=%d sdp_hdl=0x%x", + GATT_TRACE_DEBUG ("s_hdl=%d e_hdl=%d type=0x%x svc_inst=%d sdp_hdl=0x%x\n", p_sreg->s_hdl, p_sreg->e_hdl, p_sreg->type, p_sreg->service_instance, p_sreg->sdp_handle); @@ -676,16 +673,16 @@ tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, tGATT_REG *p_reg = gatt_get_regcb(gatt_if); tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); - GATT_TRACE_API ("GATTS_SendRsp: conn_id: %u trans_id: %u Status: 0x%04x", + GATT_TRACE_API ("GATTS_SendRsp: conn_id: %u trans_id: %u Status: 0x%04x\n", conn_id, trans_id, status); if ( (p_reg == NULL) || (p_tcb == NULL)) { - GATT_TRACE_ERROR ("GATTS_SendRsp Unknown conn_id: %u ", conn_id); + GATT_TRACE_ERROR ("GATTS_SendRsp Unknown conn_id: %u\n", conn_id); return (tGATT_STATUS) GATT_INVALID_CONN_ID; } if (p_tcb->sr_cmd.trans_id != trans_id) { - GATT_TRACE_ERROR ("GATTS_SendRsp conn_id: %u waiting for op_code = %02x", + GATT_TRACE_ERROR ("GATTS_SendRsp conn_id: %u waiting for op_code = %02x\n", conn_id, p_tcb->sr_cmd.op_code); return (GATT_WRONG_STATE); @@ -696,6 +693,69 @@ tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, return cmd_sent; } + +/******************************************************************************* +** +** Function GATTS_SetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle:the attribute handle +** length: the attribute length +** value: the value to be set to the attribute in the database +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value) +{ + tGATT_STATUS status; + tGATT_HDL_LIST_ELEM *p_decl = NULL; + + GATT_TRACE_DEBUG("GATTS_SetAttributeValue: attr_handle: %u length: %u \n", + attr_handle, length); + + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return GATT_INVALID_HANDLE; + } + + status = gatts_set_attribute_value(&p_decl->svc_db, attr_handle, length, value); + return status; + +} + + +/******************************************************************************* +** +** Function GATTS_GetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle: the attribute handle +** length:the attribute value length in the database +** value: the attribute value out put +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + tGATT_STATUS status; + tGATT_HDL_LIST_ELEM *p_decl; + + GATT_TRACE_DEBUG("GATTS_GetAttributeValue: attr_handle: %u\n", + attr_handle); + + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_ERROR("Service not created\n"); + return GATT_INVALID_HANDLE; + } + + status = gatts_get_attribute_value(&p_decl->svc_db, attr_handle, length, value); + return status; +} + /*******************************************************************************/ /* GATT Profile Srvr Functions */ /*******************************************************************************/ @@ -1132,7 +1192,7 @@ tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, tGATT_CBACK *p_cb_info) break; } } - GATT_TRACE_API ("allocated gatt_if=%d", gatt_if); + GATT_TRACE_API ("allocated gatt_if=%d\n", gatt_if); return gatt_if; } diff --git a/components/bt/bluedroid/stack/gatt/gatt_attr.c b/components/bt/bluedroid/stack/gatt/gatt_attr.c index 4968828394..421b17cab7 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_attr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_attr.c @@ -279,21 +279,24 @@ void gatt_profile_db_init (void) GATT_StartIf(gatt_cb.gatt_if); service_handle = GATTS_CreateService (gatt_cb.gatt_if , &uuid, 0, GATTP_MAX_ATTR_NUM, TRUE); + GATT_TRACE_ERROR ("GATTS_CreateService: handle of service handle%x", service_handle); + /* add Service Changed characteristic */ uuid.uu.uuid16 = gatt_cb.gattp_attr.uuid = GATT_UUID_GATT_SRV_CHGD; gatt_cb.gattp_attr.service_change = 0; gatt_cb.gattp_attr.handle = - gatt_cb.handle_of_h_r = GATTS_AddCharacteristic(service_handle, &uuid, 0, GATT_CHAR_PROP_BIT_INDICATE); + gatt_cb.handle_of_h_r = GATTS_AddCharacteristic(service_handle, &uuid, 0, GATT_CHAR_PROP_BIT_INDICATE, + NULL, NULL); - GATT_TRACE_DEBUG ("gatt_profile_db_init: handle of service changed%d", - gatt_cb.handle_of_h_r ); + GATT_TRACE_DEBUG ("gatt_profile_db_init: handle of service changed%d\n", + gatt_cb.handle_of_h_r); /* start service */ status = GATTS_StartService (gatt_cb.gatt_if, service_handle, GATTP_TRANSPORT_SUPPORTED ); - GATT_TRACE_DEBUG ("gatt_profile_db_init: gatt_if=%d start status%d", + GATT_TRACE_DEBUG ("gatt_profile_db_init: gatt_if=%d start status%d\n", gatt_cb.gatt_if, status); } diff --git a/components/bt/bluedroid/stack/gatt/gatt_db.c b/components/bt/bluedroid/stack/gatt/gatt_db.c index 2e74c7d2df..fd27959d96 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_db.c +++ b/components/bt/bluedroid/stack/gatt/gatt_db.c @@ -45,7 +45,7 @@ static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 le static BOOLEAN gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri); static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, - UINT16 handle, UINT16 offset, UINT32 trans_id); + UINT16 handle, UINT16 offset, UINT32 trans_id, BOOLEAN need_rsp); /******************************************************************************* ** @@ -65,12 +65,12 @@ BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN GKI_init_q(&p_db->svc_buffer); if (!allocate_svc_db_buf(p_db)) { - GATT_TRACE_ERROR("gatts_init_service_db failed, no resources"); + GATT_TRACE_ERROR("gatts_init_service_db failed, no resources\n"); return FALSE; } - GATT_TRACE_DEBUG("gatts_init_service_db"); - GATT_TRACE_DEBUG("s_hdl = %d num_handle = %d", s_hdl, num_handle ); + GATT_TRACE_DEBUG("gatts_init_service_db\n"); + GATT_TRACE_DEBUG("s_hdl = %d num_handle = %d\n", s_hdl, num_handle ); /* update service database information */ p_db->next_handle = s_hdl; @@ -94,7 +94,7 @@ BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN tBT_UUID *gatts_get_service_uuid (tGATT_SVC_DB *p_db) { if (!p_db || !p_db->p_attr_list) { - GATT_TRACE_ERROR("service DB empty"); + GATT_TRACE_ERROR("service DB empty\n"); return NULL; } else { @@ -127,28 +127,28 @@ static tGATT_STATUS gatts_check_attr_readability(tGATT_ATTR16 *p_attr, } if (!(perm & GATT_READ_ALLOWED)) { - GATT_TRACE_ERROR( "GATT_READ_NOT_PERMIT"); + GATT_TRACE_ERROR( "GATT_READ_NOT_PERMIT\n"); return GATT_READ_NOT_PERMIT; } if ((perm & GATT_READ_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED) && !(sec_flag & BTM_SEC_FLAG_ENCRYPTED)) { - GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION"); + GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION\n"); return GATT_INSUF_AUTHENTICATION; } if ((perm & GATT_READ_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED)) { - GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION: MITM Required"); + GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION: MITM Required\n"); return GATT_INSUF_AUTHENTICATION; } if ((perm & GATT_READ_ENCRYPTED_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED)) { - GATT_TRACE_ERROR( "GATT_INSUF_ENCRYPTION"); + GATT_TRACE_ERROR( "GATT_INSUF_ENCRYPTION\n"); return GATT_INSUF_ENCRYPTION; } if ( (perm & GATT_READ_ENCRYPTED_REQUIRED) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED) && (key_size < min_key_size)) { - GATT_TRACE_ERROR( "GATT_INSUF_KEY_SIZE"); + GATT_TRACE_ERROR( "GATT_INSUF_KEY_SIZE\n"); return GATT_INSUF_KEY_SIZE; } @@ -163,7 +163,7 @@ static tGATT_STATUS gatts_check_attr_readability(tGATT_ATTR16 *p_attr, case GATT_UUID_CHAR_CLIENT_CONFIG: case GATT_UUID_CHAR_SRVR_CONFIG: case GATT_UUID_CHAR_PRESENT_FORMAT: - GATT_TRACE_ERROR("GATT_NOT_LONG"); + GATT_TRACE_ERROR("GATT_NOT_LONG\n"); return GATT_NOT_LONG; default: @@ -206,7 +206,7 @@ static tGATT_STATUS read_attr_value (void *p_attr, tGATT_STATUS status; tGATT_ATTR16 *p_attr16 = (tGATT_ATTR16 *)p_attr; - GATT_TRACE_DEBUG("read_attr_value uuid=0x%04x perm=0x%0x sec_flag=0x%x offset=%d read_long=%d", + GATT_TRACE_DEBUG("read_attr_value uuid=0x%04x perm=0x%0x sec_flag=0x%x offset=%d read_long=%d\n", p_attr16->uuid, p_attr16->permission, sec_flag, @@ -268,7 +268,18 @@ static tGATT_STATUS read_attr_value (void *p_attr, status = GATT_SUCCESS; } } else { /* characteristic description or characteristic value */ - status = GATT_PENDING; + + if (p_attr16->control.auto_rsp == GATT_RSP_BY_STACK) { + if (p_attr16->p_value != NULL && p_attr16->p_value->attr_val.attr_val != NULL) { + uint8_t *value = p_attr16->p_value->attr_val.attr_val + offset; + len = (mtu >= p_attr16->p_value->attr_val.attr_len) ? (p_attr16->p_value->attr_val.attr_len) : mtu; + ARRAY_TO_STREAM(p, value, len); + } + status = GATT_STACK_RSP; + + } else { + status = GATT_PENDING; + } } *p_len = len; @@ -341,8 +352,9 @@ tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, status = read_attr_value ((void *)p_attr, 0, &p, FALSE, (UINT16)(*p_len - 2), &len, sec_flag, key_size); - if (status == GATT_PENDING) { - status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id); + if (status == GATT_PENDING || status == GATT_STACK_RSP) { + BOOLEAN need_rsp = (status != GATT_STACK_RSP); + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id, need_rsp); /* one callback at a time */ break; @@ -445,12 +457,12 @@ UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e *******************************************************************************/ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, tGATT_CHAR_PROP property, - tBT_UUID *p_char_uuid) + tBT_UUID *p_char_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) { tGATT_ATTR16 *p_char_decl, *p_char_val; tBT_UUID uuid = {LEN_UUID_16, {GATT_UUID_CHAR_DECLARE}}; - GATT_TRACE_DEBUG("gatts_add_characteristic perm=0x%0x property=0x%0x", perm, property); + GATT_TRACE_DEBUG("gatts_add_characteristic perm=0x%0x property=0x%0x\n", perm, property); if ((p_char_decl = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ)) != NULL) { if (!copy_extra_byte_in_db(p_db, (void **)&p_char_decl->p_value, sizeof(tGATT_CHAR_DECL))) { @@ -467,8 +479,30 @@ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, p_char_decl->p_value->char_decl.property = property; p_char_decl->p_value->char_decl.char_val_handle = p_char_val->handle; + if (control != NULL) { + p_char_val->control.auto_rsp = control->auto_rsp; + } else { + p_char_val->control.auto_rsp = GATT_RSP_DEFAULT; - p_char_val->p_value = NULL; + } + + if (attr_val != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_val->p_value, sizeof(tGATT_ATTR_VAL))) { + deallocate_attr_in_db(p_db, p_char_val); + return 0; + } + GATT_TRACE_DEBUG("attr_val->attr_len = %x, attr_val->attr_max_len = %x\n", attr_val->attr_len, attr_val->attr_max_len); + GATT_TRACE_DEBUG("attribute handle = %x\n", p_char_val->handle); + p_char_val->p_value->attr_val.attr_len = attr_val->attr_len; + p_char_val->p_value->attr_val.attr_max_len = attr_val->attr_max_len; + p_char_val->p_value->attr_val.attr_val = GKI_getbuf(attr_val->attr_max_len); + if (p_char_val->p_value->attr_val.attr_val != NULL) { + GATT_TRACE_DEBUG("attribute value not NULL"); + memcpy(p_char_val->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + } else { + p_char_val->p_value = NULL; + } return p_char_val->handle; } @@ -542,24 +576,227 @@ UINT8 gatt_convertchar_descr_type(tBT_UUID *p_descr_uuid) ** *******************************************************************************/ UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, - tBT_UUID *p_descr_uuid) + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) { tGATT_ATTR16 *p_char_dscptr; - GATT_TRACE_DEBUG("gatts_add_char_descr uuid=0x%04x", p_descr_uuid->uu.uuid16); + GATT_TRACE_DEBUG("gatts_add_char_descr uuid=0x%04x\n", p_descr_uuid->uu.uuid16); /* Add characteristic descriptors */ - if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, - p_descr_uuid, - perm)) - == NULL) { + if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, p_descr_uuid, perm)) == NULL) { GATT_TRACE_DEBUG("gatts_add_char_descr Fail for adding char descriptors."); return 0; } else { + if (control != NULL) { + p_char_dscptr->control.auto_rsp = control->auto_rsp; + } + if (attr_val != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_dscptr->p_value, sizeof(tGATT_ATTR_VAL))) { + deallocate_attr_in_db(p_db, p_char_dscptr); + return 0; + } + p_char_dscptr->p_value->attr_val.attr_len = attr_val->attr_len; + p_char_dscptr->p_value->attr_val.attr_max_len = attr_val->attr_max_len; + if (attr_val->attr_val != NULL) { + p_char_dscptr->p_value->attr_val.attr_val = GKI_getbuf(attr_val->attr_max_len); + if (p_char_dscptr->p_value->attr_val.attr_val != NULL) { + memset(p_char_dscptr->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + memcpy(p_char_dscptr->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + } + } return p_char_dscptr->handle; } } + +/******************************************************************************* +** +** Function gatts_set_attribute_value +** +** Description This function add the attribute value in the database +** +** Parameter p_db: database pointer. +** attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be set to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 length, UINT8 *value) +{ + tGATT_ATTR16 *p_cur; + + if (p_db == NULL) { + GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db is NULL.\n"); + return GATT_INVALID_PDU; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return GATT_INVALID_PDU; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + + while (p_cur != NULL) { + if (p_cur->handle == attr_handle) { + + if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_cur->uuid) { + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + case GATT_UUID_CHAR_DECLARE: + case GATT_UUID_INCLUDE_SERVICE: + return GATT_NOT_FOUND; + default: + if (p_cur->p_value->attr_val.attr_max_len < length) { + GATT_TRACE_ERROR("gatts_set_attribute_vaule failt:Invalid value length"); + return GATT_INVALID_ATTR_LEN; + } else { + memcpy(p_cur->p_value->attr_val.attr_val, value, length); + p_cur->p_value->attr_val.attr_len = length; + } + break; + } + } else { + if (p_cur->p_value->attr_val.attr_max_len < length) { + GATT_TRACE_ERROR("gatts_set_attribute_vaule failt:Invalid value length"); + } else { + memcpy(p_cur->p_value->attr_val.attr_val, value, length); + p_cur->p_value->attr_val.attr_len = length; + } + + } + + break; + + } + + p_cur = p_cur->p_next; + } + + return GATT_SUCCESS; +} + + +/******************************************************************************* +** +** Function gatts_get_attribute_value +** +** Description This function get the attribute value in the database +** +** Parameter p_db: database pointer. +** attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be get to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 *length, UINT8 **value) +{ + tGATT_ATTR16 *p_cur; + GATT_TRACE_DEBUG("***********%s*************\n", __func__); + GATT_TRACE_DEBUG("attr_handle = %x\n", attr_handle); + if (p_db == NULL) { + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:p_db is NULL.\n"); + return GATT_INVALID_PDU; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return GATT_INVALID_PDU; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + + while (p_cur != NULL) { + LOG_ERROR("p_ur->handle = %x\n", p_cur->handle); + if (p_cur->handle == attr_handle) { + + if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_cur->uuid) { + case GATT_UUID_CHAR_DECLARE: + case GATT_UUID_INCLUDE_SERVICE: + break; + default: + if (p_cur->p_value->attr_val.attr_len != 0) { + *length = p_cur->p_value->attr_val.attr_len; + *value = p_cur->p_value->attr_val.attr_val; + return GATT_SUCCESS; + } else { + GATT_TRACE_ERROR("gatts_get_attribute_vaule failt:the value length is 0"); + return GATT_INVALID_ATTR_LEN; + } + break; + } + } else { + if (p_cur->p_value->attr_val.attr_len != 0) { + *length = p_cur->p_value->attr_val.attr_len; + *value = p_cur->p_value->attr_val.attr_val; + return GATT_SUCCESS; + } else { + GATT_TRACE_ERROR("gatts_get_attribute_vaule failed:the value length is 0"); + return GATT_INVALID_ATTR_LEN; + } + + } + + break; + + } + + + p_cur = p_cur->p_next; + } + + return GATT_NOT_FOUND; +} + +BOOLEAN gatts_is_auto_response(UINT16 attr_handle) +{ + tGATT_HDL_LIST_ELEM *p_decl = NULL; + BOOLEAN rsp = FALSE; + tGATT_SVC_DB *p_db = NULL; + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return rsp; + } + + p_db = &p_decl->svc_db; + + tGATT_ATTR16 *p_cur, *p_next; + + if (p_db == NULL) { + GATT_TRACE_DEBUG("gatts_get_attribute_value Fail:p_db is NULL.\n"); + return rsp; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_DEBUG("gatts_get_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return rsp; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + p_next = (tGATT_ATTR16 *) p_cur->p_next; + + for (; p_cur != NULL && p_next != NULL; + p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + if (p_cur->handle == attr_handle) { + if (p_cur->p_value != NULL && p_cur->control.auto_rsp == GATT_RSP_BY_STACK) { + rsp = true; + return rsp; + } + + } + + } + + return rsp; + +} + /*******************************************************************************/ /* Service Attribute Database Query Utility Functions */ /*******************************************************************************/ @@ -605,8 +842,9 @@ tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, (BOOLEAN)(op_code == GATT_REQ_READ_BLOB), mtu, p_len, sec_flag, key_size); - if (status == GATT_PENDING) { - status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, offset, trans_id); + if ((status == GATT_PENDING) || (status == GATT_STACK_RSP)) { + BOOLEAN need_rsp = (status != GATT_STACK_RSP); + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, offset, trans_id, need_rsp); } break; } @@ -614,6 +852,42 @@ tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, } } + + return status; +} + +tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 len) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle ) { + if (p_attr->control.auto_rsp == GATT_RSP_BY_APP) { + return GATT_APP_RSP; + } + + if (p_attr->p_value != NULL && (p_attr->p_value->attr_val.attr_max_len >= + offset + len)) { + memcpy(p_attr->p_value->attr_val.attr_val + offset, p_value, len); + p_attr->p_value->attr_val.attr_len = len + offset; + return GATT_SUCCESS; + } else { + return GATT_NOT_LONG; + } + } + + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + + } + + } + return status; } @@ -661,6 +935,8 @@ tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, return status; } + + /******************************************************************************* ** ** Function gatts_write_attr_perm_check @@ -835,7 +1111,7 @@ static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PER UINT16 len = sizeof(tGATT_ATTR128); if (p_uuid == NULL) { - GATT_TRACE_ERROR("illegal UUID"); + GATT_TRACE_ERROR("illegal UUID\n"); return NULL; } @@ -845,17 +1121,17 @@ static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PER len = sizeof(tGATT_ATTR32); } - GATT_TRACE_DEBUG("allocate attr %d bytes ", len); + GATT_TRACE_DEBUG("allocate attr %d bytes\n", len); if (p_db->end_handle <= p_db->next_handle) { - GATT_TRACE_DEBUG("handle space full. handle_max = %d next_handle = %d", + GATT_TRACE_DEBUG("handle space full. handle_max = %d next_handle = %d\n", p_db->end_handle, p_db->next_handle); return NULL; } if (p_db->mem_free < len) { if (!allocate_svc_db_buf(p_db)) { - GATT_TRACE_ERROR("allocate_attr_in_db failed, no resources"); + GATT_TRACE_ERROR("allocate_attr_in_db failed, no resources\n"); return NULL; } } @@ -896,19 +1172,21 @@ static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PER } if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) { - GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid16 = [0x%04x] perm=0x%02x ", + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid16 = [0x%04x] perm=0x%02x\n", p_attr16->handle, p_attr16->uuid, p_attr16->permission); } else if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_32) { - GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid32 = [0x%08x] perm=0x%02x ", + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid32 = [0x%08x] perm=0x%02x\n", p_attr32->handle, p_attr32->uuid, p_attr32->permission); } else { - GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid128 = [0x%02x:0x%02x] perm=0x%02x ", + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid128 = [0x%02x:0x%02x] perm=0x%02x\n", p_attr128->handle, p_attr128->uuid[0], p_attr128->uuid[1], p_attr128->permission); } return (void *)p_attr16; } + + /******************************************************************************* ** ** Function deallocate_attr_in_db @@ -974,7 +1252,7 @@ static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 le if (p_db->mem_free < len) { if (!allocate_svc_db_buf(p_db)) { - GATT_TRACE_ERROR("copy_extra_byte_in_db failed, no resources"); + GATT_TRACE_ERROR("copy_extra_byte_in_db failed, no resources\n"); return FALSE; } } @@ -1028,7 +1306,7 @@ static BOOLEAN allocate_svc_db_buf(tGATT_SVC_DB *p_db) ** *******************************************************************************/ static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, - UINT16 handle, UINT16 offset, UINT32 trans_id) + UINT16 handle, UINT16 offset, UINT32 trans_id, BOOLEAN need_rsp) { tGATTS_DATA sr_data; UINT8 i_rcb; @@ -1050,6 +1328,7 @@ static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, sr_data.read_req.handle = handle; sr_data.read_req.is_long = (BOOLEAN)(op_code == GATT_REQ_READ_BLOB); sr_data.read_req.offset = offset; + sr_data.read_req.need_rsp = need_rsp; gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_READ, &sr_data); diff --git a/components/bt/bluedroid/stack/gatt/gatt_main.c b/components/bt/bluedroid/stack/gatt/gatt_main.c index 8b40ed0e51..d7997a847f 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_main.c +++ b/components/bt/bluedroid/stack/gatt/gatt_main.c @@ -508,7 +508,7 @@ static void gatt_le_data_ind (UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf) GKI_freebuf (p_buf); if (p_tcb != NULL) { - GATT_TRACE_WARNING ("ATT - Ignored L2CAP data while in state: %d", + GATT_TRACE_WARNING ("ATT - Ignored L2CAP data while in state: %d\n", gatt_get_ch_state(p_tcb)); } } @@ -906,10 +906,10 @@ void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf) } } } else { - GATT_TRACE_ERROR ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x", op_code); + GATT_TRACE_ERROR ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x\n", op_code); } } else { - GATT_TRACE_ERROR ("invalid data length, ignore"); + GATT_TRACE_ERROR ("invalid data length, ignore\n"); } GKI_freebuf (p_buf); diff --git a/components/bt/bluedroid/stack/gatt/gatt_sr.c b/components/bt/bluedroid/stack/gatt/gatt_sr.c index 4846d4ad3e..a817ad1478 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_sr.c @@ -239,7 +239,7 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, tGATT_STATUS ret_code = GATT_SUCCESS; UNUSED(trans_id); - GATT_TRACE_DEBUG("gatt_sr_process_app_rsp gatt_if=%d", gatt_if); + GATT_TRACE_DEBUG("gatt_sr_process_app_rsp gatt_if=%d\n", gatt_if); gatt_sr_update_cback_cnt(p_tcb, gatt_if, FALSE, FALSE); @@ -264,7 +264,7 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, if (p_tcb->sr_cmd.p_rsp_msg == NULL) { p_tcb->sr_cmd.p_rsp_msg = attp_build_sr_msg (p_tcb, (UINT8)(op_code + 1), (tGATT_SR_MSG *)p_msg); } else { - GATT_TRACE_ERROR("Exception!!! already has respond message"); + GATT_TRACE_ERROR("Exception!!! already has respond message\n"); } } } @@ -279,7 +279,7 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, gatt_dequeue_sr_cmd(p_tcb); } - GATT_TRACE_DEBUG("gatt_sr_process_app_rsp ret_code=%d", ret_code); + GATT_TRACE_DEBUG("gatt_sr_process_app_rsp ret_code=%d\n", ret_code); return ret_code; } @@ -371,7 +371,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U #if GATT_CONFORMANCE_TESTING == TRUE if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { - GATT_TRACE_DEBUG("Conformance tst: forced err rspvofr ReadMultiple: error status=%d", gatt_cb.err_status); + GATT_TRACE_DEBUG("Conformance tst: forced err rspvofr ReadMultiple: error status=%d\n", gatt_cb.err_status); STREAM_TO_UINT16(handle, p); @@ -871,7 +871,7 @@ static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data) ** - discover characteristic by UUID ** - relationship discovery ** -** Returns void +** Returns void ** *******************************************************************************/ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) @@ -889,10 +889,10 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, tGATT_SRV_LIST_ELEM *p_srv = NULL; reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); - + GATT_TRACE_DEBUG("%s, op_code =%x, len = %x\n", __func__, op_code, len); #if GATT_CONFORMANCE_TESTING == TRUE if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { - GATT_TRACE_DEBUG("Conformance tst: forced err rsp for ReadByType: error status=%d", gatt_cb.err_status); + GATT_TRACE_DEBUG("Conformance tst: forced err rsp for ReadByType: error status=%d\n", gatt_cb.err_status); gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, s_hdl, FALSE); @@ -902,7 +902,7 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, if (reason == GATT_SUCCESS) { if ((p_msg = (BT_HDR *)GKI_getbuf(msg_len)) == NULL) { - GATT_TRACE_ERROR("gatts_process_find_info failed. no resources."); + GATT_TRACE_ERROR("gatts_process_find_info failed. no resources.\n"); reason = GATT_NO_RESOURCES; } else { @@ -959,7 +959,7 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, p_msg->offset = L2CAP_MIN_OFFSET; } } - if (reason != GATT_SUCCESS) { + if (reason != GATT_SUCCESS && reason != GATT_STACK_RSP) { if (p_msg) { GKI_freebuf(p_msg); } @@ -987,21 +987,34 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, UINT8 op_code, UINT16 len, UINT8 *p_data) { + UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); tGATTS_DATA sr_data; UINT32 trans_id; tGATT_STATUS status; - UINT8 sec_flag, key_size, *p = p_data; + UINT8 sec_flag, key_size, *p = p_data, *p_m; tGATT_SR_REG *p_sreg; - UINT16 conn_id; - + UINT16 conn_id, offset = 0; + BT_HDR *p_msg = NULL; memset(&sr_data, 0, sizeof(tGATTS_DATA)); + if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) { + GATT_TRACE_ERROR("gatts_process_write_req failed. no resources.\n"); + } + + memset(p_msg, 0, buf_len); + p_m = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + *p_m ++ = op_code + 1; + p_msg->len = 1; + buf_len = p_tcb->payload_size - 1; + switch (op_code) { case GATT_REQ_PREPARE_WRITE: sr_data.write_req.is_prep = TRUE; STREAM_TO_UINT16(sr_data.write_req.offset, p); + UINT16_TO_STREAM(p_m, sr_data.write_req.is_prep); + offset = sr_data.write_req.offset; len -= 2; - /* fall through */ + /* fall through */ case GATT_SIGN_CMD_WRITE: if (op_code == GATT_SIGN_CMD_WRITE) { GATT_TRACE_DEBUG("Write CMD with data sigining" ); @@ -1012,11 +1025,16 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, case GATT_REQ_WRITE: if (op_code == GATT_REQ_WRITE || op_code == GATT_REQ_PREPARE_WRITE) { sr_data.write_req.need_rsp = TRUE; + if(op_code == GATT_REQ_PREPARE_WRITE){ + memcpy(p_m, p, len); + p_msg->len += len; + } } sr_data.write_req.handle = handle; sr_data.write_req.len = len; if (len != 0 && p != NULL) { memcpy (sr_data.write_req.value, p, len); + } break; } @@ -1039,48 +1057,64 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, if ((trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle)) != 0) { p_sreg = &gatt_cb.sr_reg[i_rcb]; conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); - gatt_sr_send_req_callback(conn_id, - trans_id, - GATTS_REQ_TYPE_WRITE, - &sr_data); + status = gatts_write_attr_value_by_handle(gatt_cb.sr_reg[i_rcb].p_db, + handle, offset, p, len); + if((sr_data.write_req.need_rsp == TRUE) && (status == GATT_APP_RSP)){ + sr_data.write_req.need_rsp = TRUE; + status = GATT_PENDING; + } + + else{ + sr_data.write_req.need_rsp = FALSE; + } + + gatt_sr_send_req_callback(conn_id, + trans_id, + GATTS_REQ_TYPE_WRITE, + &sr_data); + + if(status == GATT_SUCCESS){ + attp_send_sr_msg(p_tcb, p_msg); + gatt_dequeue_sr_cmd(p_tcb); + } - status = GATT_PENDING; } else { - GATT_TRACE_ERROR("max pending command, send error"); + GATT_TRACE_ERROR("max pending command, send error\n"); status = GATT_BUSY; /* max pending command, application error */ } } /* in theroy BUSY is not possible(should already been checked), protected check */ - if (status != GATT_PENDING && status != GATT_BUSY && + if (status != GATT_PENDING && status != GATT_BUSY && status != GATT_SUCCESS && (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_REQ_WRITE)) { gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); + gatt_dequeue_sr_cmd(p_tcb); } return; } /******************************************************************************* -** -** Function gatts_process_read_req -** -** Description This function is called to process the read request -** from client. -** -** Returns void -** -*******************************************************************************/ + ** + ** Function gatts_process_read_req + ** + ** Description This function is called to process the read request + ** from client. + ** + ** Returns void + ** + *******************************************************************************/ static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 op_code, UINT16 handle, UINT16 len, UINT8 *p_data) { - UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); - tGATT_STATUS reason; - BT_HDR *p_msg = NULL; - UINT8 sec_flag, key_size, *p; - UINT16 offset = 0, value_len = 0; + UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + tGATT_STATUS reason; + BT_HDR *p_msg = NULL; + UINT8 sec_flag, key_size, *p; + UINT16 offset = 0, value_len = 0; UNUSED (len); if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) { - GATT_TRACE_ERROR("gatts_process_find_info failed. no resources."); + GATT_TRACE_ERROR("gatts_process_find_info failed. no resources.\n"); reason = GATT_NO_RESOURCES; } else { @@ -1114,17 +1148,20 @@ static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 p_msg->len += value_len; } - if (reason != GATT_SUCCESS) { + + if (reason != GATT_SUCCESS && reason != GATT_PENDING && reason != GATT_STACK_RSP) { if (p_msg) { GKI_freebuf(p_msg); } /* in theroy BUSY is not possible(should already been checked), protected check */ - if (reason != GATT_PENDING && reason != GATT_BUSY) { + if (reason != GATT_BUSY) { gatt_send_error_rsp (p_tcb, reason, op_code, handle, FALSE); + gatt_dequeue_sr_cmd(p_tcb); } } else { attp_send_sr_msg(p_tcb, p_msg); + gatt_dequeue_sr_cmd(p_tcb); } } @@ -1149,7 +1186,7 @@ void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, tGATT_ATTR16 *p_attr; if (len < 2) { - GATT_TRACE_ERROR("Illegal PDU length, discard request"); + GATT_TRACE_ERROR("Illegal PDU length, discard request\n"); status = GATT_INVALID_PDU; } else { STREAM_TO_UINT16(handle, p); @@ -1159,7 +1196,7 @@ void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, #if GATT_CONFORMANCE_TESTING == TRUE gatt_cb.handle = handle; if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { - GATT_TRACE_DEBUG("Conformance tst: forced err rsp: error status=%d", gatt_cb.err_status); + GATT_TRACE_DEBUG("Conformance tst: forced err rsp: error status=%d\n", gatt_cb.err_status); gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, handle, FALSE); @@ -1350,24 +1387,24 @@ void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, /* otherwise, ignore the pkt */ } else { switch (op_code) { - case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ - case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ + case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ + case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ gatts_process_primary_service_req (p_tcb, op_code, len, p_data); break; - case GATT_REQ_FIND_INFO:/* discover char descrptor */ + case GATT_REQ_FIND_INFO: /* discover char descrptor */ gatts_process_find_info(p_tcb, op_code, len, p_data); break; - case GATT_REQ_READ_BY_TYPE: /* read characteristic value, char descriptor value */ + case GATT_REQ_READ_BY_TYPE: /* read characteristic value, char descriptor value */ /* discover characteristic, discover char by UUID */ gatts_process_read_by_type_req(p_tcb, op_code, len, p_data); break; - case GATT_REQ_READ: /* read char/char descriptor value */ + case GATT_REQ_READ: /* read char/char descriptor value */ case GATT_REQ_READ_BLOB: - case GATT_REQ_WRITE: /* write char/char descriptor value */ + case GATT_REQ_WRITE: /* write char/char descriptor value */ case GATT_CMD_WRITE: case GATT_SIGN_CMD_WRITE: case GATT_REQ_PREPARE_WRITE: diff --git a/components/bt/bluedroid/stack/gatt/gatt_utils.c b/components/bt/bluedroid/stack/gatt/gatt_utils.c index 7b55a097c9..09f4e8df2f 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/bluedroid/stack/gatt/gatt_utils.c @@ -318,6 +318,34 @@ tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle) } return NULL; } + +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_attr_handle +** +** Description Find handle range buffer by attribute handle. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_handle) +{ + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) { + if (p_list->in_use && (p_list->asgn_range.s_handle <= attr_handle) + && (p_list->asgn_range.e_handle >= attr_handle)) { + return (p_list); + } + p_list = p_list->p_next; + } + return NULL; +} + + /******************************************************************************* ** ** Function gatt_find_hdl_buffer_by_app_id @@ -1476,7 +1504,7 @@ tGATT_REG *gatt_get_regcb (tGATT_IF gatt_if) p_reg = &gatt_cb.cl_rcb[ii - 1]; if (!p_reg->in_use) { - GATT_TRACE_WARNING("gatt_if found but not in use."); + GATT_TRACE_WARNING("gatt_if found but not in use.\n"); return NULL; } diff --git a/components/bt/bluedroid/stack/gatt/include/gatt_int.h b/components/bt/bluedroid/stack/gatt/include/gatt_int.h index 1ac8431b3b..c9622a24c1 100644 --- a/components/bt/bluedroid/stack/gatt/include/gatt_int.h +++ b/components/bt/bluedroid/stack/gatt/include/gatt_int.h @@ -35,7 +35,10 @@ #define GATT_GET_GATT_IF(conn_id) ((tGATT_IF)((UINT8) (conn_id))) #define GATT_GET_SR_REG_PTR(index) (&gatt_cb.sr_reg[(UINT8) (index)]); -#define GATT_TRANS_ID_MAX 0x0fffffff /* 4 MSB is reserved */ +#define GATT_TRANS_ID_MAX 0x0fffffff /* 4 MSB is reserved */ +#define GATT_RSP_BY_APP 0x00 +#define GATT_RSP_BY_STACK 0x01 +#define GATT_RSP_DEFAULT GATT_RSP_BY_APP //need to rsp by the app. /* security action for GATT write and read request */ #define GATT_SEC_NONE 0 @@ -61,16 +64,16 @@ typedef UINT8 tGATT_SEC_ACTION; #define GATT_ATTR_OP_SPT_PREP_WRITE (0x00000001 << 10) #define GATT_ATTR_OP_SPT_EXE_WRITE (0x00000001 << 11) #define GATT_ATTR_OP_SPT_HDL_VALUE_CONF (0x00000001 << 12) -#define GATT_ATTR_OP_SP_SIGN_WRITE (0x00000001 << 13) +#define GATT_ATTR_OP_SP_SIGN_WRITE (0x00000001 << 13) -#define GATT_INDEX_INVALID 0xff +#define GATT_INDEX_INVALID 0xff -#define GATT_PENDING_REQ_NONE 0 +#define GATT_PENDING_REQ_NONE 0 -#define GATT_WRITE_CMD_MASK 0xc0 /*0x1100-0000*/ -#define GATT_AUTH_SIGN_MASK 0x80 /*0x1000-0000*/ -#define GATT_AUTH_SIGN_LEN 12 +#define GATT_WRITE_CMD_MASK 0xc0 /*0x1100-0000*/ +#define GATT_AUTH_SIGN_MASK 0x80 /*0x1000-0000*/ +#define GATT_AUTH_SIGN_LEN 12 #define GATT_HDR_SIZE 3 /* 1B opcode + 2B handle */ @@ -154,7 +157,7 @@ typedef union { tBT_UUID uuid; /* service declaration */ tGATT_CHAR_DECL char_decl; /* characteristic declaration */ tGATT_INCL_SRVC incl_handle; /* included service */ - + tGATT_ATTR_VAL attr_val; } tGATT_ATTR_VALUE; /* Attribute UUID type @@ -167,50 +170,49 @@ typedef UINT8 tGATT_ATTR_UUID_TYPE; /* 16 bits UUID Attribute in server database */ typedef struct { - void *p_next; /* pointer to the next attribute, - either tGATT_ATTR16 or tGATT_ATTR128 */ - tGATT_ATTR_VALUE *p_value; - tGATT_ATTR_UUID_TYPE uuid_type; - tGATT_PERM permission; - UINT16 handle; - UINT16 uuid; + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + UINT16 handle; + UINT16 uuid; } tGATT_ATTR16; /* 32 bits UUID Attribute in server database */ typedef struct { - void *p_next; /* pointer to the next attribute, - either tGATT_ATTR16, tGATT_ATTR32 or tGATT_ATTR128 */ - tGATT_ATTR_VALUE *p_value; - tGATT_ATTR_UUID_TYPE uuid_type; - tGATT_PERM permission; - UINT16 handle; - UINT32 uuid; + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16, tGATT_ATTR32 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + UINT16 handle; + UINT32 uuid; } tGATT_ATTR32; /* 128 bits UUID Attribute in server database */ typedef struct { - void *p_next; /* pointer to the next attribute, - either tGATT_ATTR16 or tGATT_ATTR128 */ - tGATT_ATTR_VALUE *p_value; - tGATT_ATTR_UUID_TYPE uuid_type; - tGATT_PERM permission; - UINT16 handle; - UINT8 uuid[LEN_UUID_128]; + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + UINT16 handle; + UINT8 uuid[LEN_UUID_128]; } tGATT_ATTR128; /* Service Database definition */ typedef struct { - void *p_attr_list; /* pointer to the first attribute, - either tGATT_ATTR16 or tGATT_ATTR128 */ - UINT8 *p_free_mem; /* Pointer to free memory */ - BUFFER_Q svc_buffer; /* buffer queue used for service database */ - UINT32 mem_free; /* Memory still available */ - UINT16 end_handle; /* Last handle number */ - UINT16 next_handle; /* Next usable handle value */ + void *p_attr_list; /* pointer to the first attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + UINT8 *p_free_mem; /* Pointer to free memory */ + BUFFER_Q svc_buffer; /* buffer queue used for service database */ + UINT32 mem_free; /* Memory still available */ + UINT16 end_handle; /* Last handle number */ + UINT16 next_handle; /* Next usable handle value */ } tGATT_SVC_DB; /* Data Structure used for GATT server */ @@ -218,14 +220,14 @@ typedef struct { /* A service registration information record consists of beginning and ending */ /* attribute handle, service UUID and a set of GATT server callback. */ typedef struct { - tGATT_SVC_DB *p_db; /* pointer to the service database */ + tGATT_SVC_DB *p_db; /* pointer to the service database */ tBT_UUID app_uuid; /* applicatino UUID */ - UINT32 sdp_handle; /* primamry service SDP handle */ + UINT32 sdp_handle; /* primamry service SDP handle */ UINT16 service_instance; /* service instance number */ - UINT16 type; /* service type UUID, primary or secondary */ - UINT16 s_hdl; /* service starting handle */ - UINT16 e_hdl; /* service ending handle */ - tGATT_IF gatt_if; /* this service is belong to which application */ + UINT16 type; /* service type UUID, primary or secondary */ + UINT16 s_hdl; /* service starting handle */ + UINT16 e_hdl; /* service ending handle */ + tGATT_IF gatt_if; /* this service is belong to which application */ BOOLEAN in_use; } tGATT_SR_REG; @@ -340,7 +342,7 @@ typedef struct { tGATT_CH_STATE ch_state; UINT8 ch_flags; - tGATT_IF app_hold_link[GATT_MAX_APPS]; + tGATT_IF app_hold_link[GATT_MAX_APPS]; /* server needs */ /* server response data */ @@ -350,13 +352,13 @@ typedef struct { TIMER_LIST_ENT conf_timer_ent; /* peer confirm to indication timer */ - UINT8 prep_cnt[GATT_MAX_APPS]; - UINT8 ind_count; + UINT8 prep_cnt[GATT_MAX_APPS]; + UINT8 ind_count; - tGATT_CMD_Q cl_cmd_q[GATT_CL_MAX_LCB]; - TIMER_LIST_ENT ind_ack_timer_ent; /* local app confirm to indication timer */ - UINT8 pending_cl_req; - UINT8 next_slot_inq; /* index of next available slot in queue */ + tGATT_CMD_Q cl_cmd_q[GATT_CL_MAX_LCB]; + TIMER_LIST_ENT ind_ack_timer_ent; /* local app confirm to indication timer */ + UINT8 pending_cl_req; + UINT8 next_slot_inq; /* index of next available slot in queue */ BOOLEAN in_use; UINT8 tcb_idx; @@ -579,6 +581,7 @@ extern BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb); /* reserved handle list */ extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_app_id (tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle); +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_handle); extern tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void); extern void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p); extern BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value); @@ -664,12 +667,27 @@ extern void gatt_set_sec_act(tGATT_TCB *p_tcb, tGATT_SEC_ACTION sec_act); /* gatt_db.c */ extern BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri, UINT16 s_hdl, UINT16 num_handle); extern UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e_handle, tBT_UUID service); -extern UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, tGATT_CHAR_PROP property, tBT_UUID *p_char_uuid); -extern UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, tBT_UUID *p_dscp_uuid); +extern UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tGATT_CHAR_PROP property, + tBT_UUID *p_char_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); +extern UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tBT_UUID *p_dscp_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); + +extern tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 length, UINT8 *value); + +extern tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 *length, UINT8 **value); +extern BOOLEAN gatts_is_auto_response(UINT16 attr_handle); extern tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db, UINT8 op_code, BT_HDR *p_rsp, UINT16 s_handle, UINT16 e_handle, tBT_UUID type, UINT16 *p_len, tGATT_SEC_FLAG sec_flag, UINT8 key_size, UINT32 trans_id, UINT16 *p_cur_handle); extern tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db, UINT8 op_code, UINT16 handle, UINT16 offset, UINT8 *p_value, UINT16 *p_len, UINT16 mtu, tGATT_SEC_FLAG sec_flag, UINT8 key_size, UINT32 trans_id); +extern tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 len); extern tGATT_STATUS gatts_write_attr_perm_check (tGATT_SVC_DB *p_db, UINT8 op_code, UINT16 handle, UINT16 offset, UINT8 *p_data, UINT16 len, tGATT_SEC_FLAG sec_flag, UINT8 key_size); extern tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, BOOLEAN is_long, UINT16 handle, tGATT_SEC_FLAG sec_flag, UINT8 key_size); diff --git a/components/bt/bluedroid/stack/include/gatt_api.h b/components/bt/bluedroid/stack/include/gatt_api.h index 8eea25cee7..fc8da23221 100644 --- a/components/bt/bluedroid/stack/include/gatt_api.h +++ b/components/bt/bluedroid/stack/include/gatt_api.h @@ -63,6 +63,8 @@ #define GATT_ENCRYPED_NO_MITM 0x8d #define GATT_NOT_ENCRYPTED 0x8e #define GATT_CONGESTED 0x8f +#define GATT_STACK_RSP 0x90 +#define GATT_APP_RSP 0x91 /* 0xE0 ~ 0xFC reserved for future use */ #define GATT_CCC_CFG_ERR 0xFD /* Client Characteristic Configuration Descriptor Improperly Configured */ @@ -117,36 +119,36 @@ typedef UINT16 tGATT_DISCONN_REASON; /* MAX GATT MTU size */ #ifndef GATT_MAX_MTU_SIZE -#define GATT_MAX_MTU_SIZE 517 +#define GATT_MAX_MTU_SIZE 517 #endif /* max legth of an attribute value */ #ifndef GATT_MAX_ATTR_LEN -#define GATT_MAX_ATTR_LEN 600 +#define GATT_MAX_ATTR_LEN 600 #endif /* default GATT MTU size over LE link */ -#define GATT_DEF_BLE_MTU_SIZE 23 +#define GATT_DEF_BLE_MTU_SIZE 23 /* invalid connection ID */ -#define GATT_INVALID_CONN_ID 0xFFFF +#define GATT_INVALID_CONN_ID 0xFFFF #ifndef GATT_CL_MAX_LCB - #define GATT_CL_MAX_LCB 4 // 22 +#define GATT_CL_MAX_LCB 4 // 22 #endif #ifndef GATT_MAX_SCCB - #define GATT_MAX_SCCB 4 +#define GATT_MAX_SCCB 4 #endif /* GATT notification caching timer, default to be three seconds */ #ifndef GATTC_NOTIF_TIMEOUT -#define GATTC_NOTIF_TIMEOUT 3 +#define GATTC_NOTIF_TIMEOUT 3 #endif /***************************************************************************** @@ -155,22 +157,22 @@ typedef UINT16 tGATT_DISCONN_REASON; /* Attribute permissions */ -#define GATT_PERM_READ (1 << 0) /* bit 0 */ -#define GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 */ -#define GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 */ -#define GATT_PERM_WRITE (1 << 4) /* bit 4 */ -#define GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 */ -#define GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 */ -#define GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 */ -#define GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 */ +#define GATT_PERM_READ (1 << 0) /* bit 0 */ +#define GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 */ +#define GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 */ +#define GATT_PERM_WRITE (1 << 4) /* bit 4 */ +#define GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 */ +#define GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 */ +#define GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 */ +#define GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 */ typedef UINT16 tGATT_PERM; #define GATT_ENCRYPT_KEY_SIZE_MASK (0xF000) /* the MS nibble of tGATT_PERM; key size 7=0; size 16=9 */ -#define GATT_READ_ALLOWED (GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) -#define GATT_READ_AUTH_REQUIRED (GATT_PERM_READ_ENCRYPTED) -#define GATT_READ_MITM_REQUIRED (GATT_PERM_READ_ENC_MITM) -#define GATT_READ_ENCRYPTED_REQUIRED (GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) +#define GATT_READ_ALLOWED (GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) +#define GATT_READ_AUTH_REQUIRED (GATT_PERM_READ_ENCRYPTED) +#define GATT_READ_MITM_REQUIRED (GATT_PERM_READ_ENC_MITM) +#define GATT_READ_ENCRYPTED_REQUIRED (GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) #define GATT_WRITE_ALLOWED (GATT_PERM_WRITE | GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE_ENC_MITM | \ @@ -312,6 +314,16 @@ typedef struct { UINT8 value[GATT_MAX_ATTR_LEN]; /* the actual attribute value */ } tGATT_VALUE; +typedef struct{ + UINT16 attr_max_len; + UINT16 attr_len; + UINT8 *attr_val; +}tGATT_ATTR_VAL; + +typedef struct{ + uint8_t auto_rsp; +}tGATTS_ATTR_CONTROL; + /* Union of the event data which is used in the server respond API to carry the server response information */ typedef union { @@ -337,6 +349,7 @@ typedef struct { UINT16 handle; UINT16 offset; BOOLEAN is_long; + BOOLEAN need_rsp; } tGATT_READ_REQ; /* write request data */ @@ -740,8 +753,9 @@ extern UINT16 GATTS_AddIncludeService (UINT16 service_handle, ** characteristic failed. ** *******************************************************************************/ -extern UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *char_uuid, - tGATT_PERM perm, tGATT_CHAR_PROP property); +extern UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, + tGATT_PERM perm, tGATT_CHAR_PROP property, + tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control); /******************************************************************************* ** @@ -763,7 +777,8 @@ extern UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *char_uui ** *******************************************************************************/ extern UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, tGATT_PERM perm, - tBT_UUID *p_descr_uuid); + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); /******************************************************************************* ** @@ -866,6 +881,39 @@ extern tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, tGATT_STATUS status, tGATTS_RSP *p_msg); +/******************************************************************************* +** +** Function GATTS_SetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle:the attribute handle +** length: the attribute length +** value: the value to be set to the attribute in the database +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value); + + +/******************************************************************************* +** +** Function GATTS_GetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle: the attribute handle +** length:the attribute value length in the database +** value: the attribute value out put +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value); + + + /*******************************************************************************/ /* GATT Profile Client Functions */ /*******************************************************************************/ diff --git a/components/bt/bluedroid/stack/smp/smp_main.c b/components/bt/bluedroid/stack/smp/smp_main.c index 85b232aea8..cad6427464 100644 --- a/components/bt/bluedroid/stack/smp/smp_main.c +++ b/components/bt/bluedroid/stack/smp/smp_main.c @@ -419,7 +419,7 @@ static const UINT8 smp_master_create_local_sec_conn_oob_data[][SMP_SM_NUM_COLS] static const UINT8 smp_slave_entry_map[][SMP_STATE_MAX] = { /* state name: Idle WaitApp SecReq Pair Wait Confirm Rand PublKey SCPhs1 Wait Wait SCPhs2 Wait DHKChk Enc Bond CrLocSc Rsp Pend ReqRsp Cfm Exch Strt Cmtm Nonce Strt DHKChk Pend Pend OobData */ - /* PAIR_REQ */{ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_REQ */{ 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PAIR_RSP */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* CONFIRM */{ 0, 4, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* RAND */{ 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, diff --git a/components/bt/bt.c b/components/bt/bt.c index ef9a063d69..5bad45ea2c 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -35,6 +35,15 @@ extern void btdm_osi_funcs_register(void *osi_funcs); extern void btdm_controller_init(void); +/* VHCI function interface */ +typedef struct vhci_host_callback { + void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ + int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ +} vhci_host_callback_t; + +extern bool API_vhci_host_check_send_available(void); +extern void API_vhci_host_send_packet(uint8_t *data, uint16_t len); +extern void API_vhci_host_register_callback(const vhci_host_callback_t *callback); #define BT_DEBUG(...) #define BT_API_CALL_CHECK(info, api_call, ret) \ @@ -85,7 +94,7 @@ static int32_t IRAM_ATTR semphr_give_from_isr_wrapper(void *semphr, void *hptw) static int32_t IRAM_ATTR semphr_take_wrapper(void *semphr, uint32_t block_time_ms) { - return (int32_t)xSemaphoreTake(semphr, block_time_ms / portTICK_RATE_MS); + return (int32_t)xSemaphoreTake(semphr, block_time_ms / portTICK_PERIOD_MS); } static void *IRAM_ATTR mutex_create_wrapper(void) @@ -118,13 +127,28 @@ static struct osi_funcs_t osi_funcs = { ._read_efuse_mac = esp_efuse_read_mac, }; +bool esp_vhci_host_check_send_available(void) +{ + return API_vhci_host_check_send_available(); +} + +void esp_vhci_host_send_packet(uint8_t *data, uint16_t len) +{ + API_vhci_host_send_packet(data, len); +} + +void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback) +{ + API_vhci_host_register_callback((const vhci_host_callback_t *)callback); +} + static void bt_controller_task(void *pvParam) { btdm_osi_funcs_register(&osi_funcs); btdm_controller_init(); } -void bt_controller_init() +void esp_bt_controller_init() { xTaskCreatePinnedToCore(bt_controller_task, "btController", ESP_TASK_BT_CONTROLLER_STACK, NULL, diff --git a/components/bt/component.mk b/components/bt/component.mk index a0ddd598f5..5cdcb18df2 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -1,6 +1,7 @@ # # Component Makefile # +ifdef CONFIG_BT_ENABLED COMPONENT_ADD_INCLUDEDIRS := bluedroid/bta/include \ bluedroid/bta/sys/include \ @@ -87,3 +88,5 @@ COMPONENT_SRCDIRS := bluedroid/bta/dm \ . COMPONENT_SUBMODULES += lib + +endif diff --git a/components/bt/include/bt.h b/components/bt/include/bt.h index 310fdcb921..926ecfadcd 100644 --- a/components/bt/include/bt.h +++ b/components/bt/include/bt.h @@ -29,35 +29,35 @@ extern "C" { * * This function should be called only once, before any other BT functions are called. */ -void bt_controller_init(void); +void esp_bt_controller_init(void); -/** @brief vhci_host_callback +/** @brief esp_vhci_host_callback * used for vhci call host function to notify what host need to do */ -typedef struct vhci_host_callback { +typedef struct esp_vhci_host_callback { void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ -} vhci_host_callback_t; +} esp_vhci_host_callback_t; -/** @brief API_vhci_host_check_send_available +/** @brief esp_vhci_host_check_send_available * used for check actively if the host can send packet to controller or not. * @return true for ready to send, false means cannot send packet */ -bool API_vhci_host_check_send_available(void); +bool esp_vhci_host_check_send_available(void); -/** @brief API_vhci_host_send_packet +/** @brief esp_vhci_host_send_packet * host send packet to controller * @param data the packet point *,@param len the packet length */ -void API_vhci_host_send_packet(uint8_t *data, uint16_t len); +void esp_vhci_host_send_packet(uint8_t *data, uint16_t len); -/** @brief API_vhci_host_register_callback +/** @brief esp_vhci_host_register_callback * register the vhci referece callback, the call back * struct defined by vhci_host_callback structure. - * @param callback vhci_host_callback type variable + * @param callback esp_vhci_host_callback type variable */ -void API_vhci_host_register_callback(const vhci_host_callback_t *callback); +void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback); #ifdef __cplusplus } diff --git a/components/bt/lib b/components/bt/lib index 91657e0c40..566acfd8c6 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit 91657e0c4025f5a694b0a89f449c347b0f2fdf79 +Subproject commit 566acfd8c61a4ba0fb6b9026c89488b01af0fff0 diff --git a/components/coap/Makefile.projbuild b/components/coap/Makefile.projbuild new file mode 100644 index 0000000000..e976ee32f6 --- /dev/null +++ b/components/coap/Makefile.projbuild @@ -0,0 +1 @@ +CFLAGS += -DWITH_POSIX diff --git a/components/coap/component.mk b/components/coap/component.mk new file mode 100644 index 0000000000..86993b29e9 --- /dev/null +++ b/components/coap/component.mk @@ -0,0 +1,11 @@ +# +# Component Makefile +# + +COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libcoap/include/coap + +COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_time.o libcoap/src/debug.o libcoap/src/encode.o libcoap/src/hashkey.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o port/coap_io_socket.o + +COMPONENT_SRCDIRS := libcoap/src libcoap port + +COMPONENT_SUBMODULES += libcoap diff --git a/components/coap/libcoap b/components/coap/libcoap new file mode 160000 index 0000000000..6468887a12 --- /dev/null +++ b/components/coap/libcoap @@ -0,0 +1 @@ +Subproject commit 6468887a12666f88b8704d797fc176cd4f40ee4c diff --git a/components/coap/port/coap_io_socket.c b/components/coap/port/coap_io_socket.c new file mode 100644 index 0000000000..eec8cc1319 --- /dev/null +++ b/components/coap/port/coap_io_socket.c @@ -0,0 +1,468 @@ +/* + * Network function implementation with socket for ESP32 platform. + * + * Uses libcoap software implementation for failover when concurrent + * network operations are in use. + * + * coap_io.h -- Default network I/O functions for libcoap + * + * Copyright (C) 2012,2014 Olaf Bergmann + * + * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD + * + * This file is part of the CoAP library libcoap. Please see + * README for terms of use. + */ + +#include "coap_config.h" + +#ifdef HAVE_STDIO_H +# include +#endif + +#ifdef HAVE_SYS_SELECT_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_SYS_UIO_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#include + +#ifdef WITH_CONTIKI +# include "uip.h" +#endif + +#include "pdu.h" +#include "debug.h" +#include "mem.h" +#include "coap_io.h" + +#ifdef WITH_POSIX +/* define generic PKTINFO for IPv4 */ +#if defined(IP_PKTINFO) +# define GEN_IP_PKTINFO IP_PKTINFO +#elif defined(IP_RECVDSTADDR) +# define GEN_IP_PKTINFO IP_RECVDSTADDR +#else +# error "Need IP_PKTINFO or IP_RECVDSTADDR to request ancillary data from OS." +#endif /* IP_PKTINFO */ + +/* define generic KTINFO for IPv6 */ +#ifdef IPV6_RECVPKTINFO +# define GEN_IPV6_PKTINFO IPV6_RECVPKTINFO +#elif defined(IPV6_PKTINFO) +# define GEN_IPV6_PKTINFO IPV6_PKTINFO +#else +# error "Need IPV6_PKTINFO or IPV6_RECVPKTINFO to request ancillary data from OS." +#endif /* IPV6_RECVPKTINFO */ + +struct coap_packet_t { + coap_if_handle_t hnd; /**< the interface handle */ + coap_address_t src; /**< the packet's source address */ + coap_address_t dst; /**< the packet's destination address */ + const coap_endpoint_t *interface; + + int ifindex; + void *session; /**< opaque session data */ + + size_t length; /**< length of payload */ + unsigned char payload[]; /**< payload */ +}; +#endif + +#ifdef CUSTOM_COAP_NETWORK_ENDPOINT + +#ifdef WITH_CONTIKI +static int ep_initialized = 0; + +static inline struct coap_endpoint_t * +coap_malloc_contiki_endpoint() { + static struct coap_endpoint_t ep; + + if (ep_initialized) { + return NULL; + } else { + ep_initialized = 1; + return &ep; + } +} + +static inline void +coap_free_contiki_endpoint(struct coap_endpoint_t *ep) { + ep_initialized = 0; +} + +coap_endpoint_t * +coap_new_endpoint(const coap_address_t *addr, int flags) { + struct coap_endpoint_t *ep = coap_malloc_contiki_endpoint(); + + if (ep) { + memset(ep, 0, sizeof(struct coap_endpoint_t)); + ep->handle.conn = udp_new(NULL, 0, NULL); + + if (!ep->handle.conn) { + coap_free_endpoint(ep); + return NULL; + } + + coap_address_init(&ep->addr); + uip_ipaddr_copy(&ep->addr.addr, &addr->addr); + ep->addr.port = addr->port; + udp_bind((struct uip_udp_conn *)ep->handle.conn, addr->port); + } + return ep; +} + +void +coap_free_endpoint(coap_endpoint_t *ep) { + if (ep) { + if (ep->handle.conn) { + uip_udp_remove((struct uip_udp_conn *)ep->handle.conn); + } + coap_free_contiki_endpoint(ep); + } +} + +#else /* WITH_CONTIKI */ +static inline struct coap_endpoint_t * +coap_malloc_posix_endpoint(void) { + return (struct coap_endpoint_t *)coap_malloc(sizeof(struct coap_endpoint_t)); +} + +static inline void +coap_free_posix_endpoint(struct coap_endpoint_t *ep) { + coap_free(ep); +} + +coap_endpoint_t * +coap_new_endpoint(const coap_address_t *addr, int flags) { + int sockfd = socket(addr->addr.sa.sa_family, SOCK_DGRAM, 0); + int on = 1; + struct coap_endpoint_t *ep; + + if (sockfd < 0) { + coap_log(LOG_WARNING, "coap_new_endpoint: socket"); + return NULL; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + coap_log(LOG_WARNING, "coap_new_endpoint: setsockopt SO_REUSEADDR"); + + if (bind(sockfd, &addr->addr.sa, addr->size) < 0) { + coap_log(LOG_WARNING, "coap_new_endpoint: bind"); + close (sockfd); + return NULL; + } + + ep = coap_malloc_posix_endpoint(); + if (!ep) { + coap_log(LOG_WARNING, "coap_new_endpoint: malloc"); + close(sockfd); + return NULL; + } + + memset(ep, 0, sizeof(struct coap_endpoint_t)); + ep->handle.fd = sockfd; + ep->flags = flags; + + ep->addr.size = addr->size; + if (getsockname(sockfd, &ep->addr.addr.sa, &ep->addr.size) < 0) { + coap_log(LOG_WARNING, "coap_new_endpoint: cannot determine local address"); + close (sockfd); + return NULL; + } + +#ifndef NDEBUG + if (LOG_DEBUG <= coap_get_log_level()) { +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 40 +#endif + unsigned char addr_str[INET6_ADDRSTRLEN+8]; + + if (coap_print_addr(&ep->addr, addr_str, INET6_ADDRSTRLEN+8)) { + debug("created %sendpoint %s\n", + ep->flags & COAP_ENDPOINT_DTLS ? "DTLS " : "", + addr_str); + } + } +#endif /* NDEBUG */ + + return (coap_endpoint_t *)ep; +} + +void +coap_free_endpoint(coap_endpoint_t *ep) { + if(ep) { + if (ep->handle.fd >= 0) + close(ep->handle.fd); + coap_free_posix_endpoint((struct coap_endpoint_t *)ep); + } +} + +#endif /* WITH_CONTIKI */ +#endif /* CUSTOM_COAP_NETWORK_ENDPOINT */ + +#ifdef CUSTOM_COAP_NETWORK_SEND + +#if defined(WITH_POSIX) != defined(HAVE_NETINET_IN_H) +/* define struct in6_pktinfo and struct in_pktinfo if not available + FIXME: check with configure +*/ +struct in6_pktinfo { + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface index */ +}; + +struct in_pktinfo { + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + +#if defined(WITH_POSIX) && !defined(SOL_IP) +/* Solaris expects level IPPROTO_IP for ancillary data. */ +#define SOL_IP IPPROTO_IP +#endif + +#ifdef __GNUC__ +#define UNUSED_PARAM __attribute__ ((unused)) +#else /* not a GCC */ +#define UNUSED_PARAM +#endif /* GCC */ + +ssize_t +coap_network_send(struct coap_context_t *context UNUSED_PARAM, + const coap_endpoint_t *local_interface, + const coap_address_t *dst, + unsigned char *data, + size_t datalen) { + + struct coap_endpoint_t *ep = + (struct coap_endpoint_t *)local_interface; + +#ifndef WITH_CONTIKI + return sendto(ep->handle.fd, data, datalen, 0, (struct sockaddr*)&dst->addr.sa, sizeof(struct sockaddr)); +#else /* WITH_CONTIKI */ + /* FIXME: untested */ + /* FIXME: is there a way to check if send was successful? */ + uip_udp_packet_sendto((struct uip_udp_conn *)ep->handle.conn, data, datalen, + &dst->addr, dst->port); + return datalen; +#endif /* WITH_CONTIKI */ +} + +#endif /* CUSTOM_COAP_NETWORK_SEND */ + +#ifdef CUSTOM_COAP_NETWORK_READ + +#define SIN6(A) ((struct sockaddr_in6 *)(A)) + +#ifdef WITH_POSIX +static coap_packet_t * +coap_malloc_packet(void) { + coap_packet_t *packet; + const size_t need = sizeof(coap_packet_t) + COAP_MAX_PDU_SIZE; + + packet = (coap_packet_t *)coap_malloc(need); + if (packet) { + memset(packet, 0, need); + } + return packet; +} + +void +coap_free_packet(coap_packet_t *packet) { + coap_free(packet); +} +#endif /* WITH_POSIX */ +#ifdef WITH_CONTIKI +static inline coap_packet_t * +coap_malloc_packet(void) { + return (coap_packet_t *)coap_malloc_type(COAP_PACKET, 0); +} + +void +coap_free_packet(coap_packet_t *packet) { + coap_free_type(COAP_PACKET, packet); +} +#endif /* WITH_CONTIKI */ + +static inline size_t +coap_get_max_packetlength(const coap_packet_t *packet UNUSED_PARAM) { + return COAP_MAX_PDU_SIZE; +} + +void +coap_packet_populate_endpoint(coap_packet_t *packet, coap_endpoint_t *target) +{ + target->handle = packet->interface->handle; + memcpy(&target->addr, &packet->dst, sizeof(target->addr)); + target->ifindex = packet->ifindex; + target->flags = 0; /* FIXME */ +} +void +coap_packet_copy_source(coap_packet_t *packet, coap_address_t *target) +{ + memcpy(target, &packet->src, sizeof(coap_address_t)); +} +void +coap_packet_get_memmapped(coap_packet_t *packet, unsigned char **address, size_t *length) +{ + *address = packet->payload; + *length = packet->length; +} + +/** + * Checks if a message with destination address @p dst matches the + * local interface with address @p local. This function returns @c 1 + * if @p dst is a valid match, and @c 0 otherwise. + */ +static inline int +is_local_if(const coap_address_t *local, const coap_address_t *dst) { + return coap_address_isany(local) || coap_address_equals(dst, local) || + coap_is_mcast(dst); +} + +ssize_t +coap_network_read(coap_endpoint_t *ep, coap_packet_t **packet) { + ssize_t len = -1; + +#ifdef WITH_POSIX + #define SOC_APPDATA_LEN 1460 + char *soc_appdata = NULL; + struct sockaddr_in soc_srcipaddr; + socklen_t soc_srcsize = sizeof(struct sockaddr_in); +#endif /* WITH_POSIX */ + + assert(ep); + assert(packet); + + *packet = coap_malloc_packet(); + + if (!*packet) { + warn("coap_network_read: insufficient memory, drop packet\n"); + return -1; + } + + coap_address_init(&(*packet)->dst); /* the local interface address */ + coap_address_init(&(*packet)->src); /* the remote peer */ + +#ifdef WITH_POSIX + soc_appdata = coap_malloc(SOC_APPDATA_LEN); + if (soc_appdata){ + len = recvfrom(ep->handle.fd, soc_appdata, SOC_APPDATA_LEN, 0, (struct sockaddr *)&soc_srcipaddr, (socklen_t *)&soc_srcsize); + + if (len < 0){ + coap_log(LOG_WARNING, "coap_network_read: %s\n", strerror(errno)); + goto error; + } else { + /* use getsockname() to get the local port */ + (*packet)->dst.size = sizeof((*packet)->dst.addr); + if (getsockname(ep->handle.fd, &(*packet)->dst.addr.sa, &(*packet)->dst.size) < 0) { + coap_log(LOG_DEBUG, "cannot determine local port\n"); + goto error; + } + + /* local interface for IPv4 */ + (*packet)->src.size = sizeof((*packet)->src.addr); + memcpy(&(*packet)->src.addr.sa, &soc_srcipaddr, (*packet)->src.size); + + if (len > coap_get_max_packetlength(*packet)) { + /* FIXME: we might want to send back a response */ + warn("discarded oversized packet\n"); + goto error; + } + + if (!is_local_if(&ep->addr, &(*packet)->dst)) { + coap_log(LOG_DEBUG, "packet received on wrong interface, dropped\n"); + printf("error 3\n"); + goto error; + } + + (*packet)->length = len; + + memcpy(&(*packet)->payload, soc_appdata, len); + } + + coap_free(soc_appdata); + soc_appdata = NULL; + } else { + goto error; + } +#endif /* WITH_POSIX */ +#ifdef WITH_CONTIKI + /* FIXME: untested, make this work */ +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLIPH_LEN]) + + if(uip_newdata()) { + uip_ipaddr_copy(&(*packet)->src.addr, &UIP_IP_BUF->srcipaddr); + (*packet)->src.port = UIP_UDP_BUF->srcport; + uip_ipaddr_copy(&(*packet)->dst.addr, &UIP_IP_BUF->destipaddr); + (*packet)->dst.port = UIP_UDP_BUF->destport; + + if (!is_local_if(&ep->addr, &(*packet)->dst)) { + coap_log(LOG_DEBUG, "packet received on wrong interface, dropped\n"); + goto error; + } + + len = uip_datalen(); + + if (len > coap_get_max_packetlength(*packet)) { + /* FIXME: we might want to send back a response */ + warn("discarded oversized packet\n"); + return -1; + } + + ((char *)uip_appdata)[len] = 0; +#ifndef NDEBUG + if (LOG_DEBUG <= coap_get_log_level()) { +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 40 +#endif + unsigned char addr_str[INET6_ADDRSTRLEN+8]; + + if (coap_print_addr(&(*packet)->src, addr_str, INET6_ADDRSTRLEN+8)) { + debug("received %zd bytes from %s\n", len, addr_str); + } + } +#endif /* NDEBUG */ + + (*packet)->length = len; + memcpy(&(*packet)->payload, uip_appdata, len); + } + +#undef UIP_IP_BUF +#undef UIP_UDP_BUF +#endif /* WITH_CONTIKI */ +#ifdef WITH_LWIP +#error "coap_network_read() not implemented on this platform" +#endif + + (*packet)->interface = ep; + + return len; + error: +#ifdef WITH_POSIX + if (soc_appdata) + coap_free(soc_appdata); + soc_appdata = NULL; +#endif + coap_free_packet(*packet); + *packet = NULL; + return -1; +} + +#undef SIN6 + +#endif /* CUSTOM_COAP_NETWORK_READ */ diff --git a/components/coap/port/include/coap/coap.h b/components/coap/port/include/coap/coap.h new file mode 100644 index 0000000000..cbdc9dfc81 --- /dev/null +++ b/components/coap/port/include/coap/coap.h @@ -0,0 +1,50 @@ +/* Modify head file implementation for ESP32 platform. + * + * Uses libcoap software implementation for failover when concurrent + * define operations are in use. + * + * coap.h -- main header file for CoAP stack of libcoap + * + * Copyright (C) 2010-2012,2015-2016 Olaf Bergmann + * 2015 Carsten Schoenert + * + * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +#ifndef _COAP_H_ +#define _COAP_H_ + +#include "libcoap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "address.h" +#include "async.h" +#include "bits.h" +#include "block.h" +#include "coap_io.h" +#include "coap_time.h" +#include "debug.h" +#include "encode.h" +#include "mem.h" +#include "net.h" +#include "option.h" +#include "pdu.h" +#include "prng.h" +#include "resource.h" +#include "str.h" +#include "subscribe.h" +#include "uri.h" +#include "uthash.h" +#include "utlist.h" + +#ifdef __cplusplus +} +#endif + +#endif /* _COAP_H_ */ diff --git a/components/coap/port/include/coap_config.h b/components/coap/port/include/coap_config.h new file mode 100644 index 0000000000..db314f2de9 --- /dev/null +++ b/components/coap/port/include/coap_config.h @@ -0,0 +1,39 @@ +/* + * libcoap configure implementation for ESP32 platform. + * + * Uses libcoap software implementation for failover when concurrent + * configure operations are in use. + * + * coap.h -- main header file for CoAP stack of libcoap + * + * Copyright (C) 2010-2012,2015-2016 Olaf Bergmann + * 2015 Carsten Schoenert + * + * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#ifdef WITH_POSIX +#include "coap_config_posix.h" +#endif + +#define HAVE_STDIO_H +#define HAVE_ASSERT_H + +#define PACKAGE_STRING PACKAGE_NAME PACKAGE_VERSION + +/* it's just provided by libc. i hope we don't get too many of those, as + * actually we'd need autotools again to find out what environment we're + * building in */ +#define HAVE_STRNLEN 1 + +#define HAVE_LIMITS_H + +#define COAP_RESOURCES_NOHASH + +#endif /* _CONFIG_H_ */ diff --git a/components/coap/port/include/coap_config_posix.h b/components/coap/port/include/coap_config_posix.h new file mode 100644 index 0000000000..a77e97f074 --- /dev/null +++ b/components/coap/port/include/coap_config_posix.h @@ -0,0 +1,41 @@ +/* + * libcoap configure implementation for ESP32 platform. + * + * Uses libcoap software implementation for failover when concurrent + * configure operations are in use. + * + * coap.h -- main header file for CoAP stack of libcoap + * + * Copyright (C) 2010-2012,2015-2016 Olaf Bergmann + * 2015 Carsten Schoenert + * + * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +#ifndef COAP_CONFIG_POSIX_H_ +#define COAP_CONFIG_POSIX_H_ + +#ifdef WITH_POSIX + +#include + +#define HAVE_SYS_SOCKET_H +#define HAVE_MALLOC +#define HAVE_ARPA_INET_H + +#define IP_PKTINFO IP_MULTICAST_IF +#define IPV6_PKTINFO IPV6_V6ONLY + +#define PACKAGE_NAME "libcoap-posix" +#define PACKAGE_VERSION "?" + +#define CUSTOM_COAP_NETWORK_ENDPOINT +#define CUSTOM_COAP_NETWORK_SEND +#define CUSTOM_COAP_NETWORK_READ + +#endif + +#endif /* COAP_CONFIG_POSIX_H_ */ diff --git a/components/cxx/component.mk b/components/cxx/component.mk new file mode 100644 index 0000000000..3ce43fc328 --- /dev/null +++ b/components/cxx/component.mk @@ -0,0 +1,3 @@ +# Mark __cxa_guard_dummy as undefined so that implementation of static guards +# is taken from cxx_guards.o instead of libstdc++.a +COMPONENT_ADD_LDFLAGS := -l$(COMPONENT_NAME) -u __cxa_guard_dummy diff --git a/components/cxx/cxx_guards.cpp b/components/cxx/cxx_guards.cpp new file mode 100644 index 0000000000..288ff3ea03 --- /dev/null +++ b/components/cxx/cxx_guards.cpp @@ -0,0 +1,216 @@ +// Copyright 2015-2016 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. + +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +using __cxxabiv1::__guard; + +static SemaphoreHandle_t s_static_init_mutex = NULL; //!< lock used for the critical section +static SemaphoreHandle_t s_static_init_wait_sem = NULL; //!< counting semaphore used by the waiting tasks +static portMUX_TYPE s_init_spinlock = portMUX_INITIALIZER_UNLOCKED; //!< spinlock used to guard initialization of the above two primitives +static size_t s_static_init_waiting_count = 0; //!< number of tasks which are waiting for static init guards +#ifndef _NDEBUG +static size_t s_static_init_max_waiting_count = 0; //!< maximum ever value of the above; can be inspected using GDB for debugging purposes +#endif + + +/** + * Layout of the guard object (defined by the ABI). + * + * Compiler will check lower byte before calling guard functions. + */ +typedef struct { + uint8_t ready; //!< nonzero if initialization is done + uint8_t pending; //!< nonzero if initialization is in progress +} guard_t; + +static void static_init_prepare() +{ + portENTER_CRITICAL(&s_init_spinlock); + if (s_static_init_mutex == NULL) { + s_static_init_mutex = xSemaphoreCreateMutex(); + s_static_init_wait_sem = xSemaphoreCreateCounting(INT_MAX, 0); + if (s_static_init_mutex == NULL || s_static_init_wait_sem == NULL) { + // no way to bail out of static initialization without these + abort(); + } + } + portEXIT_CRITICAL(&s_init_spinlock); +} + +/** + * Use s_static_init_wait_sem to wait until guard->pending == 0. + * Preconditions: + * - s_static_init_mutex taken + * - guard.pending == 1 + * Postconditions: + * - s_static_init_mutex taken + * - guard.pending == 0 + */ +static void wait_for_guard_obj(guard_t* g) +{ + s_static_init_waiting_count++; +#ifndef _NDEBUG + s_static_init_max_waiting_count = std::max(s_static_init_waiting_count, + s_static_init_max_waiting_count); +#endif + + do { + auto result = xSemaphoreGive(s_static_init_mutex); + assert(result); + /* Task may be preempted here, but this isn't a problem, + * as the semaphore will be given exactly the s_static_init_waiting_count + * number of times; eventually the current task will execute next statement, + * which will immediately succeed. + */ + result = xSemaphoreTake(s_static_init_wait_sem, portMAX_DELAY); + assert(result); + /* At this point the semaphore was given, so all waiting tasks have woken up. + * We take s_static_init_mutex before accessing the state of the guard + * object again. + */ + result = xSemaphoreTake(s_static_init_mutex, portMAX_DELAY); + assert(result); + /* Semaphore may have been given because some other guard object became ready. + * Check the guard object we need and wait again if it is still pending. + */ + } while(g->pending); + s_static_init_waiting_count--; +} + +/** + * Unblock tasks waiting for static initialization to complete. + * Preconditions: + * - s_static_init_mutex taken + * Postconditions: + * - s_static_init_mutex taken + */ +static void signal_waiting_tasks() +{ + auto count = s_static_init_waiting_count; + while (count--) { + xSemaphoreGive(s_static_init_wait_sem); + } +} + +extern "C" int __cxa_guard_acquire(__guard* pg) +{ + guard_t* g = reinterpret_cast(pg); + const auto scheduler_started = xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED; + if (!scheduler_started) { + if (g->pending) { + /* Before the scheduler has started, there we don't support simultaneous + * static initialization. This may be implemented using a spinlock and a + * s32c1i instruction, though. + */ + abort(); + } + } else { + if (s_static_init_mutex == NULL) { + static_init_prepare(); + } + + /* We don't need to use double-checked locking pattern here, as the compiler + * must generate code to check if the first byte of *pg is non-zero, before + * calling __cxa_guard_acquire. + */ + auto result = xSemaphoreTake(s_static_init_mutex, portMAX_DELAY); + assert(result); + if (g->pending) { + /* Another task is doing initialization at the moment; wait until it calls + * __cxa_guard_release or __cxa_guard_abort + */ + wait_for_guard_obj(g); + /* At this point there are two scenarios: + * - the task which was doing static initialization has called __cxa_guard_release, + * which means that g->ready is set. We need to return 0. + * - the task which was doing static initialization has called __cxa_guard_abort, + * which means that g->ready is not set; we should acquire the guard and return 1, + * same as for the case if we didn't have to wait. + * Note: actually the second scenario is unlikely to occur in the current + * configuration because exception support is disabled. + */ + } + } + int ret; + if (g->ready) { + /* Static initialization has been done by another task; nothing to do here */ + ret = 0; + } else { + /* Current task can start doing static initialization */ + g->pending = 1; + ret = 1; + } + if (scheduler_started) { + auto result = xSemaphoreGive(s_static_init_mutex); + assert(result); + } + return ret; +} + +extern "C" void __cxa_guard_release(__guard* pg) +{ + guard_t* g = reinterpret_cast(pg); + const auto scheduler_started = xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED; + if (scheduler_started) { + auto result = xSemaphoreTake(s_static_init_mutex, portMAX_DELAY); + assert(result); + } + assert(g->pending && "tried to release a guard which wasn't acquired"); + g->pending = 0; + /* Initialization was successful */ + g->ready = 1; + if (scheduler_started) { + /* Unblock the tasks waiting for static initialization to complete */ + signal_waiting_tasks(); + auto result = xSemaphoreGive(s_static_init_mutex); + assert(result); + } +} + +extern "C" void __cxa_guard_abort(__guard* pg) +{ + guard_t* g = reinterpret_cast(pg); + const auto scheduler_started = xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED; + if (scheduler_started) { + auto result = xSemaphoreTake(s_static_init_mutex, portMAX_DELAY); + assert(result); + } + assert(!g->ready && "tried to abort a guard which is ready"); + assert(g->pending && "tried to release a guard which is not acquired"); + g->pending = 0; + if (scheduler_started) { + /* Unblock the tasks waiting for static initialization to complete */ + signal_waiting_tasks(); + auto result = xSemaphoreGive(s_static_init_mutex); + assert(result); + } +} + +/** + * Dummy function used to force linking this file instead of the same one in libstdc++. + * This works via -u __cxa_guard_dummy flag in component.mk + */ +extern "C" void __cxa_guard_dummy() +{ +} diff --git a/components/cxx/test/component.mk b/components/cxx/test/component.mk new file mode 100644 index 0000000000..ce464a212a --- /dev/null +++ b/components/cxx/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/cxx/test/test_cxx.cpp b/components/cxx/test/test_cxx.cpp new file mode 100644 index 0000000000..3b653361f4 --- /dev/null +++ b/components/cxx/test/test_cxx.cpp @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include "unity.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +static const char* TAG = "cxx"; + +TEST_CASE("can use new and delete", "[cxx]") +{ + int* int_p = new int(10); + delete int_p; + int* int_array = new int[10]; + delete[] int_array; +} + +class Base +{ +public: + virtual ~Base() {} + virtual void foo() = 0; +}; + +class Derived : public Base +{ +public: + virtual void foo() { } +}; + +TEST_CASE("can call virtual functions", "[cxx]") +{ + Derived d; + Base& b = static_cast(d); + b.foo(); +} + +class NonPOD +{ +public: + NonPOD(int a_) : a(a_) { } + int a; +}; + +static int non_pod_test_helper(int new_val) +{ + static NonPOD non_pod(42); + int ret = non_pod.a; + non_pod.a = new_val; + return ret; +} + +TEST_CASE("can use static initializers for non-POD types", "[cxx]") +{ + TEST_ASSERT_EQUAL(42, non_pod_test_helper(1)); + TEST_ASSERT_EQUAL(1, non_pod_test_helper(0)); +} + +TEST_CASE("can call std::function and bind", "[cxx]") +{ + int outer = 1; + std::function fn = [&outer](int x) -> int { + return x + outer; + }; + outer = 5; + TEST_ASSERT_EQUAL(6, fn(1)); + + auto bound = std::bind(fn, outer); + outer = 10; + TEST_ASSERT_EQUAL(15, bound()); +} + +TEST_CASE("can use std::vector", "[cxx]") +{ + std::vector v(10, 1); + v[0] = 42; + TEST_ASSERT_EQUAL(51, std::accumulate(std::begin(v), std::end(v), 0)); +} + +/* + * This test exercises static initialization guards for two objects. + * For each object, 4 tasks are created which attempt to perform static initialization. + * We check that constructor runs only once for each object. + */ + +static SemaphoreHandle_t s_slow_init_sem = NULL; + +template +class SlowInit +{ +public: + SlowInit(int arg) { + ESP_LOGD(TAG, "init obj=%d start, arg=%d\n", obj, arg); + vTaskDelay(300/portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(-1, mInitBy); + TEST_ASSERT_EQUAL(0, mInitCount); + mInitBy = arg; + ++mInitCount; + ESP_LOGD(TAG, "init obj=%d done\n", obj); + } + + static void task(void* arg) { + int taskId = reinterpret_cast(arg); + ESP_LOGD(TAG, "obj=%d before static init, task=%d\n", obj, taskId); + static SlowInit slowinit(taskId); + ESP_LOGD(TAG, "obj=%d after static init, task=%d\n", obj, taskId); + xSemaphoreGive(s_slow_init_sem); + vTaskDelay(10); + vTaskDelete(NULL); + } +private: + static int mInitBy; + static int mInitCount; +}; + +template<> int SlowInit<1>::mInitBy = -1; +template<> int SlowInit<1>::mInitCount = 0; +template<> int SlowInit<2>::mInitBy = -1; +template<> int SlowInit<2>::mInitCount = 0; + +template +static void start_slow_init_task(int id, int affinity) +{ + xTaskCreatePinnedToCore(&SlowInit::task, "slow_init", 2048, + reinterpret_cast(id), 3, NULL, affinity); +} + +TEST_CASE("static initialization guards work as expected", "[cxx]") +{ + s_slow_init_sem = xSemaphoreCreateCounting(10, 0); + TEST_ASSERT_NOT_NULL(s_slow_init_sem); + // four tasks competing for static initialization of one object + start_slow_init_task<1>(0, PRO_CPU_NUM); + start_slow_init_task<1>(1, APP_CPU_NUM); + start_slow_init_task<1>(2, PRO_CPU_NUM); + start_slow_init_task<1>(3, tskNO_AFFINITY); + + // four tasks competing for static initialization of another object + start_slow_init_task<2>(0, PRO_CPU_NUM); + start_slow_init_task<2>(1, APP_CPU_NUM); + start_slow_init_task<2>(2, PRO_CPU_NUM); + start_slow_init_task<2>(3, tskNO_AFFINITY); + + // All tasks should + for (int i = 0; i < 8; ++i) { + TEST_ASSERT_TRUE(xSemaphoreTake(s_slow_init_sem, 500/portTICK_PERIOD_MS)); + } + vSemaphoreDelete(s_slow_init_sem); +} + +struct GlobalInitTest +{ + GlobalInitTest() : index(order++) { + } + int index; + static int order; +}; + +int GlobalInitTest::order = 0; + +GlobalInitTest g_init_test1; +GlobalInitTest g_init_test2; +GlobalInitTest g_init_test3; + +TEST_CASE("global initializers run in the correct order", "[cxx]") +{ + TEST_ASSERT_EQUAL(0, g_init_test1.index); + TEST_ASSERT_EQUAL(1, g_init_test2.index); + TEST_ASSERT_EQUAL(2, g_init_test3.index); +} + +struct StaticInitTestBeforeScheduler +{ + StaticInitTestBeforeScheduler() + { + static int first_init_order = getOrder(); + index = first_init_order; + } + + int getOrder() + { + return order++; + } + + int index; + static int order; +}; + +int StaticInitTestBeforeScheduler::order = 1; + +StaticInitTestBeforeScheduler g_static_init_test1; +StaticInitTestBeforeScheduler g_static_init_test2; +StaticInitTestBeforeScheduler g_static_init_test3; + +TEST_CASE("before scheduler has started, static initializers work correctly", "[cxx]") +{ + TEST_ASSERT_EQUAL(1, g_static_init_test1.index); + TEST_ASSERT_EQUAL(1, g_static_init_test2.index); + TEST_ASSERT_EQUAL(1, g_static_init_test3.index); + TEST_ASSERT_EQUAL(2, StaticInitTestBeforeScheduler::order); +} + + +TEST_CASE("can use iostreams", "[cxx]") +{ + std::cout << "hello world"; +} diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 1a38620dbb..4e83705408 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -72,45 +72,59 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_39 }; -esp_err_t gpio_pullup_en(gpio_num_t gpio_num) { +typedef struct { + gpio_isr_t fn; /*!< isr function */ + void* args; /*!< isr function args */ +} gpio_isr_func_t; + +static gpio_isr_func_t* gpio_isr_func = NULL; +static gpio_isr_handle_t gpio_isr_handle; +static portMUX_TYPE gpio_spinlock = portMUX_INITIALIZER_UNLOCKED; + +esp_err_t gpio_pullup_en(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { rtc_gpio_pullup_en(gpio_num); - }else{ + } else { REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU); } return ESP_OK; } -esp_err_t gpio_pullup_dis(gpio_num_t gpio_num) { +esp_err_t gpio_pullup_dis(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { rtc_gpio_pullup_dis(gpio_num); - }else{ + } else { REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU); } return ESP_OK; } -esp_err_t gpio_pulldown_en(gpio_num_t gpio_num) { +esp_err_t gpio_pulldown_en(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { rtc_gpio_pulldown_en(gpio_num); - }else{ - REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD); + } else { + REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD); } return ESP_OK; } -esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num) { +esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ - rtc_gpio_pulldown_dis(gpio_num); - }else{ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { + rtc_gpio_pulldown_dis(gpio_num); + } else { REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD); } return ESP_OK; } + esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) { GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); @@ -161,7 +175,7 @@ static esp_err_t gpio_output_enable(gpio_num_t gpio_num) esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG); if (level) { if (gpio_num < 32) { GPIO.out_w1ts = (1 << gpio_num); @@ -323,6 +337,96 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) return ESP_OK; } +void IRAM_ATTR gpio_intr_service(void* arg) +{ + //GPIO intr process + uint32_t gpio_num = 0; + //read status to get interrupt status for GPIO0-31 + uint32_t gpio_intr_status; + gpio_intr_status = GPIO.status; + //read status1 to get interrupt status for GPIO32-39 + uint32_t gpio_intr_status_h; + gpio_intr_status_h = GPIO.status1.intr_st; + + if (gpio_isr_func == NULL) { + return; + } + do { + if (gpio_num < 32) { + if (gpio_intr_status & BIT(gpio_num)) { //gpio0-gpio31 + if (gpio_isr_func[gpio_num].fn != NULL) { + gpio_isr_func[gpio_num].fn(gpio_isr_func[gpio_num].args); + } + GPIO.status_w1tc = BIT(gpio_num); + } + } else { + if (gpio_intr_status_h & BIT(gpio_num - 32)) { + if (gpio_isr_func[gpio_num].fn != NULL) { + gpio_isr_func[gpio_num].fn(gpio_isr_func[gpio_num].args); + } + GPIO.status1_w1tc.intr_st = BIT(gpio_num - 32); + } + } + } while (++gpio_num < GPIO_PIN_COUNT); +} + +esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void* args) +{ + GPIO_CHECK(gpio_isr_func != NULL, "GPIO isr service is not installed, call gpio_install_isr_service() first", ESP_ERR_INVALID_STATE); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&gpio_spinlock); + gpio_intr_disable(gpio_num); + if (gpio_isr_func) { + gpio_isr_func[gpio_num].fn = isr_handler; + gpio_isr_func[gpio_num].args = args; + } + gpio_intr_enable(gpio_num); + portEXIT_CRITICAL(&gpio_spinlock); + return ESP_OK; +} + +esp_err_t gpio_isr_handler_remove(gpio_num_t gpio_num) +{ + GPIO_CHECK(gpio_isr_func != NULL, "GPIO isr service is not installed, call gpio_install_isr_service() first", ESP_ERR_INVALID_STATE); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&gpio_spinlock); + gpio_intr_disable(gpio_num); + if (gpio_isr_func) { + gpio_isr_func[gpio_num].fn = NULL; + gpio_isr_func[gpio_num].args = NULL; + } + portEXIT_CRITICAL(&gpio_spinlock); + return ESP_OK; +} + +esp_err_t gpio_install_isr_service(int intr_alloc_flags) +{ + GPIO_CHECK(gpio_isr_func == NULL, "GPIO isr service already installed", ESP_FAIL); + esp_err_t ret; + portENTER_CRITICAL(&gpio_spinlock); + gpio_isr_func = (gpio_isr_func_t*) calloc(GPIO_NUM_MAX, sizeof(gpio_isr_func_t)); + if (gpio_isr_func == NULL) { + ret = ESP_ERR_NO_MEM; + } else { + ret = gpio_isr_register(gpio_intr_service, NULL, intr_alloc_flags, &gpio_isr_handle); + } + portEXIT_CRITICAL(&gpio_spinlock); + return ret; +} + +void gpio_uninstall_isr_service() +{ + if (gpio_isr_func == NULL) { + return; + } + portENTER_CRITICAL(&gpio_spinlock); + esp_intr_free(gpio_isr_handle); + free(gpio_isr_func); + gpio_isr_func = NULL; + portEXIT_CRITICAL(&gpio_spinlock); + return; +} + esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle) { GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG); @@ -334,7 +438,7 @@ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) { GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; - if ((intr_type == GPIO_INTR_LOW_LEVEL) || (intr_type == GPIO_INTR_HIGH_LEVEL)) { + if (( intr_type == GPIO_INTR_LOW_LEVEL ) || ( intr_type == GPIO_INTR_HIGH_LEVEL )) { GPIO.pin[gpio_num].int_type = intr_type; GPIO.pin[gpio_num].wakeup_enable = 0x1; } else { diff --git a/components/driver/i2c.c b/components/driver/i2c.c new file mode 100644 index 0000000000..39eb877349 --- /dev/null +++ b/components/driver/i2c.c @@ -0,0 +1,1037 @@ +// Copyright 2015-2016 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. +#include +#include +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_log.h" +#include "malloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/dport_reg.h" +#include "soc/i2c_struct.h" +#include "soc/i2c_reg.h" +#include "driver/i2c.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" + +static const char* I2C_TAG = "i2c"; +#define I2C_CHECK(a, str, ret) if(!(a)) { \ + ESP_LOGE(I2C_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } + +static portMUX_TYPE i2c_spinlock[I2C_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; +/* DRAM_ATTR is required to avoid I2C array placed in flash, due to accessed from ISR */ +static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 }; + + +#define I2C_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) +#define I2C_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) +#define I2C_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) +#define I2C_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + +#define I2C_DRIVER_ERR_STR "i2c driver install error" +#define I2C_DRIVER_MALLOC_ERR_STR "i2c driver malloc error" +#define I2C_NUM_ERROR_STR "i2c number error" +#define I2C_ADDR_ERROR_STR "i2c null address error" +#define I2C_DRIVER_NOT_INSTALL_ERR_STR "i2c driver not installed" +#define I2C_SLAVE_BUFFER_LEN_ERR_STR "i2c buffer size too short for slave mode" +#define I2C_EVT_QUEUE_ERR_STR "i2c evt queue error" +#define I2C_SEM_ERR_STR "i2c semaphore error" +#define I2C_BUF_ERR_STR "i2c ringbuffer error" +#define I2C_MASTER_MODE_ERR_STR "Only allowed in master mode" +#define I2C_MODE_SLAVE_ERR_STR "Only allowed in slave mode" +#define I2C_CMD_MALLOC_ERR_STR "i2c command link malloc error" +#define I2C_TRANS_MODE_ERR_STR "i2c trans mode error" +#define I2C_MODE_ERR_STR "i2c mode error" +#define I2C_SDA_IO_ERR_STR "sda gpio number error" +#define I2C_SCL_IO_ERR_STR "scl gpio number error" +#define I2C_CMD_LINK_INIT_ERR_STR "i2c command link error" +#define I2C_GPIO_PULLUP_ERR_STR "this i2c pin do not support internal pull-up" +#define I2C_FIFO_FULL_THRESH_VAL (28) +#define I2C_FIFO_EMPTY_THRESH_VAL (5) + +typedef struct { + uint8_t byte_num; /*!< cmd byte number */ + uint8_t ack_en; /*!< ack check enable */ + uint8_t ack_exp; /*!< expected ack level to get */ + uint8_t ack_val; /*!< ack value to send */ + uint8_t* data; /*!< data address */ + uint8_t byte_cmd; /*!< to save cmd for one byte command mode */ + i2c_opmode_t op_code; /*!< haredware cmd type */ +}i2c_cmd_t; + +typedef struct i2c_cmd_link{ + i2c_cmd_t cmd; /*!< command in current cmd link */ + struct i2c_cmd_link *next; /*!< next cmd link */ +} i2c_cmd_link_t; + +typedef struct { + i2c_cmd_link_t* head; /*!< head of the command link */ + i2c_cmd_link_t* cur; /*!< last node of the command link */ + i2c_cmd_link_t* free; /*!< the first node to free of the command link */ +} i2c_cmd_desc_t; + +typedef enum { + I2C_STATUS_READ, /*!< read status for current master command */ + I2C_STATUS_WRITE, /*!< write status for current master command */ + I2C_STATUS_IDLE, /*!< idle status for current master command */ + I2C_STATUS_ACK_ERROR, /*!< ack error status for current master command */ + I2C_STATUS_DONE, /*!< I2C command done */ +} i2c_status_t; + +typedef struct { + int i2c_num; /*!< I2C port number */ + int mode; /*!< I2C mode, master or slave */ + intr_handle_t intr_handle; /*!< I2C interrupt handle*/ + + int cmd_idx; /*!< record current command index, for master mode */ + int status; /*!< record current command status, for master mode */ + int rx_cnt; /*!< record current read index, for master mode */ + uint8_t data_buf[I2C_FIFO_LEN]; /*!< a buffer to store i2c fifo data */ + i2c_cmd_desc_t cmd_link; /*!< I2C command link */ + xSemaphoreHandle cmd_sem; /*!< semaphore to sync command status */ + xSemaphoreHandle cmd_mux; /*!< semaphore to lock command process */ + size_t tx_fifo_remain; /*!< tx fifo remain length, for master mode */ + size_t rx_fifo_remain; /*!< rx fifo remain length, for master mode */ + + xSemaphoreHandle slv_rx_mux; /*!< slave rx buffer mux */ + xSemaphoreHandle slv_tx_mux; /*!< slave tx buffer mux */ + size_t rx_buf_length; /*!< rx buffer length */ + RingbufHandle_t rx_ring_buf; /*!< rx ringbuffer handler of slave mode */ + size_t tx_buf_length; /*!< tx buffer length */ + RingbufHandle_t tx_ring_buf; /*!< tx ringbuffer handler of slave mode */ +} i2c_obj_t; + +static i2c_obj_t *p_i2c_obj[I2C_NUM_MAX] = {0}; +static void i2c_isr_handler_default(void* arg); +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num); + +/* + For i2c master mode, we don't need to use a buffer for the data, the APIs will execute the master commands +and return after all of the commands have been sent out or when error occurs. So when we send master commands, +we should free or modify the source data only after the i2c_master_cmd_begin function returns. + For i2c slave mode, we need a data buffer to stash the sending and receiving data, because the hardware fifo +has only 32 bytes. +*/ + +esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_buf_len, size_t slv_tx_buf_len, + int intr_alloc_flags) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(mode == I2C_MODE_MASTER || ( slv_rx_buf_len > 100 || slv_tx_buf_len > 100 ), I2C_SLAVE_BUFFER_LEN_ERR_STR, + ESP_ERR_INVALID_ARG); + uint32_t intr_mask = 0; + if (p_i2c_obj[i2c_num] == NULL) { + p_i2c_obj[i2c_num] = (i2c_obj_t*) calloc(1, sizeof(i2c_obj_t)); + if (p_i2c_obj[i2c_num] == NULL) { + ESP_LOGE(I2C_TAG, I2C_DRIVER_MALLOC_ERR_STR); + return ESP_FAIL; + } + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + p_i2c->i2c_num = i2c_num; + p_i2c->mode = mode; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->status = I2C_STATUS_IDLE; + + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + + if (mode == I2C_MODE_SLAVE) { + //we only use ringbuffer for slave mode. + if (slv_rx_buf_len > 0) { + p_i2c->rx_ring_buf = xRingbufferCreate(slv_rx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->rx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->rx_buf_length = slv_rx_buf_len; + } else { + p_i2c->tx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (slv_tx_buf_len > 0) { + p_i2c->tx_ring_buf = xRingbufferCreate(slv_tx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->tx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->tx_buf_length = slv_tx_buf_len; + } else { + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + p_i2c->slv_rx_mux = xSemaphoreCreateMutex(); + p_i2c->slv_tx_mux = xSemaphoreCreateMutex(); + if (p_i2c->slv_rx_mux == NULL || p_i2c->slv_rx_mux == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + intr_mask |= ( I2C_RXFIFO_FULL_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M ); + } else { + //semaphore to sync sending process, because we only have 32 bytes for hardware fifo. + p_i2c->cmd_sem = xSemaphoreCreateBinary(); + p_i2c->cmd_mux = xSemaphoreCreateMutex(); + if (p_i2c->cmd_sem == NULL || p_i2c->cmd_mux == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + //command link + p_i2c->cmd_link.cur = NULL; + p_i2c->cmd_link.head = NULL; + p_i2c->cmd_link.free = NULL; + + p_i2c->tx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + } else { + ESP_LOGE(I2C_TAG, I2C_DRIVER_ERR_STR); + return ESP_FAIL; + } + //hook isr handler + i2c_isr_register(i2c_num, i2c_isr_handler_default, p_i2c_obj[i2c_num], intr_alloc_flags, &p_i2c_obj[i2c_num]->intr_handle); + intr_mask |= ( I2C_TRANS_COMPLETE_INT_ENA_M | + I2C_TRANS_START_INT_ENA_M | + I2C_ARBITRATION_LOST_INT_ENA_M | + I2C_ACK_ERR_INT_ENA_M | + I2C_RXFIFO_OVF_INT_ENA_M | + I2C_SLAVE_TRAN_COMP_INT_ENA_M ); + SET_PERI_REG_MASK(I2C_INT_ENA_REG(i2c_num), intr_mask); + return ESP_OK; + + err: + //Some error has happened. Free/destroy all allocated things and return ESP_FAIL. + if (p_i2c_obj[i2c_num]) { + if (p_i2c_obj[i2c_num]->rx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->rx_ring_buf); + p_i2c_obj[i2c_num]->rx_ring_buf = NULL; + p_i2c_obj[i2c_num]->rx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->tx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->tx_ring_buf); + p_i2c_obj[i2c_num]->tx_ring_buf = NULL; + p_i2c_obj[i2c_num]->tx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->cmd_sem) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_sem); + } + if (p_i2c_obj[i2c_num]->cmd_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_mux); + } + if (p_i2c_obj[i2c_num]->slv_rx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_rx_mux); + } + if (p_i2c_obj[i2c_num]->slv_tx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_tx_mux); + } + } + free(p_i2c_obj[i2c_num]); + return ESP_FAIL; +} + +esp_err_t i2c_driver_delete(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + if (p_i2c->cmd_mux) { + xSemaphoreTake(p_i2c->cmd_mux, portMAX_DELAY); + vSemaphoreDelete(p_i2c->cmd_mux); + } + if (p_i2c->cmd_sem) { + vSemaphoreDelete(p_i2c->cmd_sem); + } + if (p_i2c->slv_rx_mux) { + vSemaphoreDelete(p_i2c->slv_rx_mux); + } + if (p_i2c->slv_tx_mux) { + vSemaphoreDelete(p_i2c->slv_tx_mux); + } + + if (p_i2c->rx_ring_buf) { + vRingbufferDelete(p_i2c->rx_ring_buf); + p_i2c->rx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (p_i2c->tx_ring_buf) { + vRingbufferDelete(p_i2c->tx_ring_buf); + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + uint32_t intr_mask = I2C_MASTER_TRAN_COMP_INT_ENA_M | + I2C_TIME_OUT_INT_ENA_M | + I2C_TRANS_COMPLETE_INT_ENA_M | + I2C_TRANS_START_INT_ENA_M | + I2C_TX_SEND_EMPTY_INT_ENA_M | + I2C_ARBITRATION_LOST_INT_ENA_M | + I2C_ACK_ERR_INT_ENA_M | + I2C_RXFIFO_OVF_INT_ENA_M | + I2C_RX_REC_FULL_INT_ENA_M | + I2C_SLAVE_TRAN_COMP_INT_ENA_M; + CLEAR_PERI_REG_MASK(I2C_INT_ENA_REG(i2c_num), intr_mask); + esp_intr_free(p_i2c->intr_handle); + p_i2c->intr_handle = NULL; + free(p_i2c_obj[i2c_num]); + p_i2c_obj[i2c_num] = NULL; + return ESP_OK; +} + +esp_err_t i2c_reset_tx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_reset_rx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +static void i2c_isr_handler_default(void* arg) +{ + i2c_obj_t* p_i2c = (i2c_obj_t*) arg; + int i2c_num = p_i2c->i2c_num; + uint32_t status = I2C[i2c_num]->int_status.val; + int idx = 0; + portBASE_TYPE HPTaskAwoken = pdFALSE; + while (status != 0) { + status = I2C[i2c_num]->int_status.val; + if (status & I2C_TX_SEND_EMPTY_INT_ST_M) { + I2C[i2c_num]->int_clr.tx_send_empty = 1; + } else if (status & I2C_RX_REC_FULL_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_rec_full = 1; + } else if (status & I2C_ACK_ERR_INT_ST_M) { + I2C[i2c_num]->int_clr.ack_err = 1; + if (p_i2c->mode == I2C_MODE_MASTER) { + p_i2c_obj[i2c_num]->status = I2C_STATUS_ACK_ERROR; + I2C[i2c_num]->int_clr.ack_err = 1; + //get error ack value from slave device, stop the commands + i2c_master_cmd_begin_static(i2c_num); + } + } else if (status & I2C_TRANS_START_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_start = 1; + } else if (status & I2C_TIME_OUT_INT_ST_M) { + I2C[i2c_num]->int_clr.time_out = 1; + } else if (status & I2C_TRANS_COMPLETE_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_complete = 1; + if (p_i2c->mode == I2C_MODE_SLAVE) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + } else { + if (p_i2c->status != I2C_STATUS_ACK_ERROR) { + i2c_master_cmd_begin_static(i2c_num); + } + } + } else if (status & I2C_MASTER_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.master_tran_comp = 1; + } else if (status & I2C_ARBITRATION_LOST_INT_ST_M) { + I2C[i2c_num]->int_clr.arbitration_lost = 1; + } else if (status & I2C_SLAVE_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.slave_tran_comp = 1; + } else if (status & I2C_END_DETECT_INT_ST_M) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + i2c_master_cmd_begin_static(i2c_num); + } else if (status & I2C_RXFIFO_OVF_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_fifo_ovf = 1; + } else if (status & I2C_TXFIFO_EMPTY_INT_ST_M) { + int tx_fifo_rem = I2C_FIFO_LEN - I2C[i2c_num]->status_reg.tx_fifo_cnt; + size_t size = 0; + uint8_t *data = (uint8_t*) xRingbufferReceiveUpToFromISR(p_i2c->tx_ring_buf, &size, tx_fifo_rem); + if (data) { + for (idx = 0; idx < size; idx++) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), data[idx]); + } + vRingbufferReturnItemFromISR(p_i2c->tx_ring_buf, data, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } else { + I2C[i2c_num]->int_ena.tx_fifo_empty = 0; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } + } else if (status & I2C_RXFIFO_FULL_INT_ST_M) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + } else { + I2C[i2c_num]->int_clr.val = status; + } + } +} + +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(tx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(rx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = rx_trans_mode; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = tx_trans_mode; //set tx data msb first + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + if (tx_trans_mode) { + *tx_trans_mode = I2C[i2c_num]->ctr.tx_lsb_first; + } + if (rx_trans_mode) { + *rx_trans_mode = I2C[i2c_num]->ctr.rx_lsb_first; + } + return ESP_OK; +} + +esp_err_t i2c_param_config(i2c_port_t i2c_num, i2c_config_t* i2c_conf) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf->mode < I2C_MODE_MAX, I2C_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + + esp_err_t ret = i2c_set_pin(i2c_num, i2c_conf->sda_io_num, i2c_conf->scl_io_num, + i2c_conf->sda_pullup_en, i2c_conf->scl_pullup_en, i2c_conf->mode); + if (ret != ESP_OK) { + return ret; + } + if (i2c_num == I2C_NUM_0) { + periph_module_enable(PERIPH_I2C0_MODULE); + } else if (i2c_num == I2C_NUM_1) { + periph_module_enable(PERIPH_I2C1_MODULE); + } + + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set tx data msb first + I2C[i2c_num]->ctr.ms_mode = i2c_conf->mode; //mode for master or slave + I2C[i2c_num]->ctr.sda_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.scl_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.sample_scl_level = 0; //sample at high level of clock + + if (i2c_conf->mode == I2C_MODE_SLAVE) { //slave mode + I2C[i2c_num]->slave_addr.addr = i2c_conf->slave.slave_addr; + I2C[i2c_num]->slave_addr.en_10bit = i2c_conf->slave.addr_10bit_en; + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + I2C[i2c_num]->fifo_conf.fifo_addr_cfg_en = 0; + I2C[i2c_num]->fifo_conf.rx_fifo_full_thrhd = I2C_FIFO_FULL_THRESH_VAL; + I2C[i2c_num]->fifo_conf.tx_fifo_empty_thrhd = I2C_FIFO_EMPTY_THRESH_VAL; + I2C[i2c_num]->int_ena.rx_fifo_full = 1; + I2C[i2c_num]->ctr.trans_start = 0; + } else { + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + } + //set frequency + int half_cycle = ( I2C_APB_CLK_FREQ / i2c_conf->master.clk_speed ) / 2; + I2C[i2c_num]->scl_low_period.period = half_cycle - 1; + I2C[i2c_num]->scl_high_period.period = ( I2C_APB_CLK_FREQ / i2c_conf->master.clk_speed ) - half_cycle - 1; + //set timing for start signal + I2C[i2c_num]->scl_start_hold.time = half_cycle; + I2C[i2c_num]->scl_rstart_setup.time = half_cycle; + //set timing for stop signal + I2C[i2c_num]->scl_stop_hold.time = half_cycle; + I2C[i2c_num]->scl_stop_setup.time = half_cycle; + //set timing for data + I2C[i2c_num]->sda_hold.time = half_cycle / 2; + I2C[i2c_num]->sda_sample.time = half_cycle / 2; + //set timeout of receving data + I2C[i2c_num]->timeout.tout = 200000; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->scl_high_period.period = high_period; + I2C[i2c_num]->scl_low_period.period = low_period; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_period(i2c_port_t i2c_num, int* high_period, int* low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (high_period) { + *high_period = I2C[i2c_num]->scl_high_period.period; + } + if (low_period) { + *low_period = I2C[i2c_num]->scl_low_period.period; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->scl_start_hold.time = hold_time; + I2C[i2c_num]->scl_rstart_setup.time = setup_time; + return ESP_OK; +} + +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_start_hold.time; + } + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_rstart_setup.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->scl_stop_hold.time = hold_time; + I2C[i2c_num]->scl_stop_setup.time = setup_time; + return ESP_OK; +} + +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_stop_setup.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_stop_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->sda_hold.time = hold_time; + I2C[i2c_num]->sda_sample.time = sample_time; + return ESP_OK; +} + +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (sample_time) { + *sample_time = I2C[i2c_num]->sda_sample.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->sda_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_isr_register(i2c_port_t i2c_num, void (*fn)(void*), void * arg, int intr_alloc_flags, intr_handle_t *handle) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(fn != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + esp_err_t ret; + switch (i2c_num) { + case I2C_NUM_1: + ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + case I2C_NUM_0: + default: + ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + } + return ret; +} + +esp_err_t i2c_isr_free(intr_handle_t handle) +{ + return esp_intr_free(handle); +} + +esp_err_t i2c_set_pin(i2c_port_t i2c_num, gpio_num_t sda_io_num, gpio_num_t scl_io_num, gpio_pullup_t sda_pullup_en, gpio_pullup_t scl_pullup_en, i2c_mode_t mode) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(((GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num))), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + (GPIO_IS_VALID_GPIO(scl_io_num) && mode == I2C_MODE_SLAVE), + I2C_SCL_IO_ERR_STR, + ESP_ERR_INVALID_ARG); + I2C_CHECK((sda_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num)) || + sda_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((scl_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + scl_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + + int sda_in_sig, sda_out_sig, scl_in_sig, scl_out_sig; + switch (i2c_num) { + case I2C_NUM_1: + sda_out_sig = I2CEXT1_SDA_OUT_IDX; + sda_in_sig = I2CEXT1_SDA_IN_IDX; + scl_out_sig = I2CEXT1_SCL_OUT_IDX; + scl_in_sig = I2CEXT1_SCL_IN_IDX; + break; + case I2C_NUM_0: + default: + sda_out_sig = I2CEXT0_SDA_OUT_IDX; + sda_in_sig = I2CEXT0_SDA_IN_IDX; + scl_out_sig = I2CEXT0_SCL_OUT_IDX; + scl_in_sig = I2CEXT0_SCL_IN_IDX; + break; + } + if (sda_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[sda_io_num], PIN_FUNC_GPIO); + gpio_set_direction(sda_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + if (sda_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(sda_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(sda_io_num, GPIO_FLOATING); + } + gpio_matrix_out(sda_io_num, sda_out_sig, 0, 0); + gpio_matrix_in(sda_io_num, sda_in_sig, 0); + } + + if (scl_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[scl_io_num], PIN_FUNC_GPIO); + if (mode == I2C_MODE_MASTER) { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + gpio_matrix_out(scl_io_num, scl_out_sig, 0, 0); + } else { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT); + } + if (scl_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(scl_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(scl_io_num, GPIO_FLOATING); + } + gpio_matrix_in(scl_io_num, scl_in_sig, 0); + } + return ESP_OK; +} + +i2c_cmd_handle_t i2c_cmd_link_create() +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) calloc(1, sizeof(i2c_cmd_desc_t)); + return (i2c_cmd_handle_t) cmd_desc; +} + +void i2c_cmd_link_delete(i2c_cmd_handle_t cmd_handle) +{ + if (cmd_handle == NULL) { + return; + } + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + while (cmd->free) { + i2c_cmd_link_t* ptmp = cmd->free; + cmd->free = cmd->free->next; + free(ptmp); + } + cmd->cur = NULL; + cmd->free = NULL; + cmd->head = NULL; + free(cmd_handle); + return; +} + +static esp_err_t i2c_cmd_link_append(i2c_cmd_handle_t cmd_handle, i2c_cmd_t* cmd) +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) cmd_handle; + if (cmd_desc->head == NULL) { + cmd_desc->head = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->head == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->head; + cmd_desc->free = cmd_desc->head; + } else { + cmd_desc->cur->next = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->cur->next == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->cur->next; + } + memcpy((uint8_t*) &cmd_desc->cur->cmd, (uint8_t*) cmd, sizeof(i2c_cmd_t)); + cmd_desc->cur->next = NULL; + return ESP_OK; + + err: + return ESP_FAIL; +} + +esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_RESTART; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_stop(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_STOP; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, bool ack_en) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + uint8_t len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool ack_en) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = NULL; + cmd.byte_cmd = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, int ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + int len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ack & 0x1; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_READ; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, int ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ack & 0x1; + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_READ; + cmd.data = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE HPTaskAwoken = pdFALSE; + //This should never happen + if (p_i2c->mode == I2C_MODE_SLAVE) { + return; + } + if (p_i2c->status == I2C_STATUS_DONE) { + return; + } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + I2C[i2c_num]->int_ena.master_tran_comp = 0; + xSemaphoreGiveFromISR(p_i2c->cmd_sem, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + return; + } else if (p_i2c->status == I2C_STATUS_READ) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + while (p_i2c->rx_cnt-- > 0) { + *cmd->data++ = READ_PERI_REG(I2C_DATA_APB_REG(i2c_num)); + } + if (cmd->byte_num > 0) { + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + } + if (p_i2c->cmd_link.head == NULL) { + p_i2c->cmd_link.cur = NULL; + xSemaphoreGiveFromISR(p_i2c->cmd_sem, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + return; + } + while (p_i2c->cmd_link.head) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + I2C[i2c_num]->command[p_i2c->cmd_idx].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_en = cmd->ack_en; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_exp = cmd->ack_exp; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = cmd->byte_num; + I2C[i2c_num]->command[p_i2c->cmd_idx].op_code = cmd->op_code; + if (cmd->op_code == I2C_CMD_WRITE) { + //TODO: to reduce interrupt number + if (cmd->data) { + while (p_i2c->tx_fifo_remain > 0 && cmd->byte_num > 0) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), *cmd->data++); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + } + } else { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), cmd->byte_cmd); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + } + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num -= cmd->byte_num; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + if (cmd->byte_num > 0) { + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + p_i2c->status = I2C_STATUS_WRITE; + break; + } else if(cmd->op_code == I2C_CMD_READ) { + //TODO: to reduce interrupt number + p_i2c->rx_cnt = cmd->byte_num > p_i2c->rx_fifo_remain ? p_i2c->rx_fifo_remain : cmd->byte_num; + cmd->byte_num -= p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->status = I2C_STATUS_READ; + break; + } else { + } + p_i2c->cmd_idx++; + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + if (p_i2c->cmd_link.head == NULL || p_i2c->cmd_idx >= 15) { + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + p_i2c->status = I2C_STATUS_IDLE; + break; + } + } + I2C[i2c_num]->int_clr.end_detect = 1; + I2C[i2c_num]->int_clr.master_tran_comp = 1; + I2C[i2c_num]->int_ena.end_detect = 1; + I2C[i2c_num]->int_ena.master_tran_comp = 1; + I2C[i2c_num]->ctr.trans_start = 0; + I2C[i2c_num]->ctr.trans_start = 1; + return; +} + +esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_NOT_INSTALL_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_MASTER, I2C_MASTER_MODE_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + esp_err_t ret; + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + portBASE_TYPE res = xSemaphoreTake(p_i2c->cmd_mux, ticks_to_wait); + if (res == pdFALSE) { + return ESP_ERR_TIMEOUT; + } + xSemaphoreTake(p_i2c->cmd_sem, 0); + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + p_i2c->cmd_link.free = cmd->free; + p_i2c->cmd_link.cur = cmd->cur; + p_i2c->cmd_link.head = cmd->head; + p_i2c->status = I2C_STATUS_IDLE; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + + //start send commands, at most 32 bytes one time, isr handler will process the remaining commands. + i2c_master_cmd_begin_static(i2c_num); + ticks_to_wait = ticks_end - xTaskGetTickCount(); + res = xSemaphoreTake(p_i2c->cmd_sem, ticks_to_wait); + if (res == pdFALSE) { + ret = ESP_ERR_TIMEOUT; + } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) { + ret = ESP_FAIL; + } else { + ret = ESP_OK; + } + p_i2c->status = I2C_STATUS_DONE; + xSemaphoreGive(p_i2c->cmd_mux); + return ret; +} + +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + + portBASE_TYPE res; + int cnt = 0; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + + res = xSemaphoreTake(p_i2c->slv_tx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + res = xRingbufferSend(p_i2c->tx_ring_buf, data, size, ticks_to_wait); + if (res == pdFALSE) { + cnt = 0; + } else { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + cnt = size; + } + xSemaphoreGive(p_i2c->slv_tx_mux); + return cnt; +} + +static int i2c_slave_read(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + size_t size; + uint8_t* pdata = (uint8_t*) xRingbufferReceiveUpTo(p_i2c->rx_ring_buf, &size, ticks_to_wait, max_size); + if (pdata && size > 0) { + memcpy(data, pdata, size); + vRingbufferReturnItem(p_i2c->rx_ring_buf, pdata); + } + return size; +} + +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE res; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + res = xSemaphoreTake(p_i2c->slv_rx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + int cnt = i2c_slave_read(i2c_num, data, max_size, ticks_to_wait); + if (cnt > 0) { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_ena.rx_fifo_full = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + ticks_to_wait = ticks_end - xTaskGetTickCount(); + if (cnt < max_size && ticks_to_wait > 0) { + cnt += i2c_slave_read(i2c_num, data + cnt, max_size - cnt, ticks_to_wait); + } + } else { + cnt = 0; + } + xSemaphoreGive(p_i2c->slv_rx_mux); + return cnt; +} + + + + diff --git a/components/driver/i2s.c b/components/driver/i2s.c new file mode 100644 index 0000000000..d8e6198370 --- /dev/null +++ b/components/driver/i2s.c @@ -0,0 +1,807 @@ +// Copyright 2015-2016 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. +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" + +#include "soc/dport_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/sens_reg.h" +#include "rom/lldesc.h" + +#include "driver/gpio.h" +#include "driver/i2s.h" + +#include "esp_intr.h" +#include "esp_err.h" +#include "esp_log.h" + +static const char* I2S_TAG = "I2S"; +#define I2S_CHECK(a, str, ret) if (!(a)) { \ + ESP_LOGE(I2S_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } +#define I2S_BASE_CLK (2*APB_CLK_FREQ) +#define I2S_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_spinlock[i2s_num]) +#define I2S_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) +#define gpio_matrix_out_check(a, b, c, d) if(a != -1) gpio_matrix_out(a, b, c, d) //if pin = -1, do not need to configure +#define gpio_matrix_in_check(a, b, c) if(a != -1) gpio_matrix_in(a, b, c) + + +/** + * @brief DMA buffer object + * + */ +typedef struct { + char **buf; + int buf_size; + int rw_pos; + void *curr_ptr; + SemaphoreHandle_t mux; + xQueueHandle queue; + lldesc_t **desc; +} i2s_dma_t; + +/** + * @brief I2S object instance + * + */ +typedef struct { + i2s_port_t i2s_num; /*!< I2S port number*/ + int queue_size; /*!< I2S event queue size*/ + QueueHandle_t i2s_queue; /*!< I2S queue handler*/ + int dma_buf_count; /*!< DMA buffer count, number of buffer*/ + int dma_buf_len; /*!< DMA buffer length, length of each buffer*/ + i2s_dma_t *rx; /*!< DMA Tx buffer*/ + i2s_dma_t *tx; /*!< DMA Rx buffer*/ + i2s_isr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/ + int channel_num; /*!< Number of channels*/ + int bytes_per_sample; /*!< Bytes per sample*/ + i2s_mode_t mode; /*!< I2S Working mode*/ +} i2s_obj_t; + +static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0}; +static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1}; +static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; +static esp_err_t i2s_reset_fifo(i2s_port_t i2s_num) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->conf.rx_fifo_reset = 1; + I2S[i2s_num]->conf.rx_fifo_reset = 0; + I2S[i2s_num]->conf.tx_fifo_reset = 1; + I2S[i2s_num]->conf.tx_fifo_reset = 0; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_clear_intr_status(i2s_port_t i2s_num, uint32_t clr_mask) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S[i2s_num]->int_clr.val = clr_mask; + return ESP_OK; +} + +esp_err_t i2s_enable_rx_intr(i2s_port_t i2s_num) +{ + + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.in_suc_eof = 1; + I2S[i2s_num]->int_ena.in_dscr_err = 1; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_disable_rx_intr(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.in_suc_eof = 0; + I2S[i2s_num]->int_ena.in_dscr_err = 0; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_disable_tx_intr(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.out_eof = 0; + I2S[i2s_num]->int_ena.out_dscr_err = 0; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_enable_tx_intr(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.out_eof = 1; + I2S[i2s_num]->int_ena.out_dscr_err = 1; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +static esp_err_t i2s_isr_register(i2s_port_t i2s_num, uint8_t intr_alloc_flags, void (*fn)(void*), void * arg, i2s_isr_handle_t *handle) +{ + return esp_intr_alloc(ETS_I2S0_INTR_SOURCE + i2s_num, intr_alloc_flags, fn, arg, handle); +} + +static esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint8_t bits, bool fuzzy) +{ + int factor = (256%bits)? 384 : 256; // According to hardware codec requirement(supported 256fs or 384fs) + int clkmInteger, clkmDecimals, bck = 0; + float denom = (float)1 / 64; + int channel = 2; + + float clkmdiv = (float)I2S_BASE_CLK / (rate * factor); + if (clkmdiv > 256) { + ESP_LOGE(I2S_TAG, "clkmdiv is too large\r\n"); + return ESP_FAIL; + } + clkmInteger = clkmdiv; + clkmDecimals = (clkmdiv - clkmInteger) / denom; + float mclk = clkmInteger + denom * clkmDecimals; + bck = factor/(bits * channel); + + I2S[i2s_num]->clkm_conf.clka_en = 0; + I2S[i2s_num]->clkm_conf.clkm_div_a = 63; + I2S[i2s_num]->clkm_conf.clkm_div_b = clkmDecimals; + I2S[i2s_num]->clkm_conf.clkm_div_num = clkmInteger; + I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = bck; + I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = bck; + I2S[i2s_num]->sample_rate_conf.tx_bits_mod = bits; + I2S[i2s_num]->sample_rate_conf.rx_bits_mod = bits; + float real_rate = (float)(I2S_BASE_CLK / (bck * bits * clkmInteger)/2); + ESP_LOGI(I2S_TAG, "Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", + rate, real_rate, bits, clkmInteger, bck, (float)I2S_BASE_CLK / mclk, real_rate *16*2, 64, clkmDecimals); + return ESP_OK; +} + +static void IRAM_ATTR i2s_intr_handler_default(void *arg) +{ + i2s_obj_t *p_i2s = (i2s_obj_t*) arg; + uint8_t i2s_num = p_i2s->i2s_num; + i2s_dev_t* i2s_reg = I2S[i2s_num]; + i2s_event_t i2s_event; + int dummy; + + portBASE_TYPE high_priority_task_awoken = 0; + + lldesc_t *finish_desc; + + if (i2s_reg->int_st.out_dscr_err || i2s_reg->int_st.in_dscr_err) { + ESP_LOGE(I2S_TAG, "out_dscr_err: %d or in_dscr_err:%d", i2s_reg->int_st.out_dscr_err == 1, i2s_reg->int_st.in_dscr_err == 1); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_DMA_ERROR; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + + if (i2s_reg->int_st.out_eof && p_i2s->tx) { + finish_desc = (lldesc_t*) i2s_reg->out_eof_des_addr; + // All buffers are empty. This means we have an underflow on our hands. + if (xQueueIsQueueFullFromISR(p_i2s->tx->queue)) { + xQueueReceiveFromISR(p_i2s->tx->queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->tx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_TX_DONE; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + + } + + if (i2s_reg->int_st.in_suc_eof && p_i2s->rx) { + // All buffers are full. This means we have an overflow. + finish_desc = (lldesc_t*) i2s_reg->in_eof_des_addr; + if (xQueueIsQueueFullFromISR(p_i2s->rx->queue)) { + xQueueReceiveFromISR(p_i2s->rx->queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->rx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_RX_DONE; + if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + if (high_priority_task_awoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + + i2s_reg->int_clr.val = I2S[i2s_num]->int_st.val; +} + +static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma) +{ + int bux_idx; + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGE(I2S_TAG, "Not initialized yet"); + return ESP_FAIL; + } + if (dma == NULL) { + return ESP_FAIL; + } + for (bux_idx = 0; bux_idx < p_i2s_obj[i2s_num]->dma_buf_count; bux_idx++) { + if (dma->desc && dma->desc[bux_idx]) + free(dma->desc[bux_idx]); + if (dma->buf && dma->buf[bux_idx]) + free(dma->buf[bux_idx]); + } + if (dma->buf) + free(dma->buf); + if (dma->desc) + free(dma->desc); + vQueueDelete(dma->queue); + vSemaphoreDelete(dma->mux); + return ESP_OK; +} + +static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) +{ + int bux_idx; + int sample_size = p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + i2s_dma_t *dma = (i2s_dma_t*) malloc(sizeof(i2s_dma_t)); + if (dma == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc i2s_dma_t"); + return NULL; + } + memset(dma, 0, sizeof(i2s_dma_t)); + + dma->buf = (char **)malloc(sizeof(char*) * dma_buf_count); + if (dma->buf == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma buffer pointer"); + + return NULL; + } + memset(dma->buf, 0, sizeof(char*) * dma_buf_count); + + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->buf[bux_idx] = (char*) malloc(dma_buf_len * sample_size); + if (dma->buf[bux_idx] == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma buffer"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + ESP_LOGD(I2S_TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); + memset(dma->buf[bux_idx], 0, dma_buf_len * sample_size); + } + + dma->desc = (lldesc_t**) malloc(sizeof(lldesc_t*) * dma_buf_count); + if (dma->desc == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma description"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->desc[bux_idx] = (lldesc_t*) malloc(sizeof(lldesc_t)); + if (dma->desc[bux_idx] == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma description entry"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + } + + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->desc[bux_idx]->owner = 1; + dma->desc[bux_idx]->eof = 1; + dma->desc[bux_idx]->sosf = 0; + dma->desc[bux_idx]->length = dma_buf_len * sample_size; + dma->desc[bux_idx]->size = dma_buf_len * sample_size; + dma->desc[bux_idx]->buf = (uint8_t *) dma->buf[bux_idx]; + dma->desc[bux_idx]->offset = 0; + dma->desc[bux_idx]->empty = (uint32_t)((bux_idx < (dma_buf_count - 1)) ? (dma->desc[bux_idx + 1]) : dma->desc[0]); + } + dma->queue = xQueueCreate(dma_buf_count - 1, sizeof(char*)); + dma->mux = xSemaphoreCreateMutex(); + dma->rw_pos = 0; + dma->buf_size = dma_buf_len * sample_size; + dma->curr_ptr = NULL; + ESP_LOGI(I2S_TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", dma_buf_len * sample_size, dma_buf_count); + return dma; +} + + +esp_err_t i2s_start(i2s_port_t i2s_num) +{ + //start DMA link + I2S_ENTER_CRITICAL(); + esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); + I2S[i2s_num]->int_clr.val = 0xFFFFFFFF; + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + ESP_LOGD(I2S_TAG, "I2S_MODE_TX"); + i2s_enable_tx_intr(i2s_num); + I2S[i2s_num]->out_link.start = 1; + I2S[i2s_num]->conf.tx_start = 1; + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + ESP_LOGD(I2S_TAG, "I2S_MODE_RX"); + i2s_enable_rx_intr(i2s_num); + I2S[i2s_num]->in_link.start = 1; + I2S[i2s_num]->conf.rx_start = 1; + } + esp_intr_enable(p_i2s_obj[i2s_num]->i2s_isr_handle); + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_stop(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + I2S[i2s_num]->out_link.stop = 1; + I2S[i2s_num]->conf.tx_start = 0; + i2s_disable_tx_intr(i2s_num); + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + I2S[i2s_num]->in_link.stop = 1; + I2S[i2s_num]->conf.rx_start = 0; + i2s_disable_rx_intr(i2s_num); + } + I2S_EXIT_CRITICAL(); + return 0; +} + +static esp_err_t configure_dac_pin(void) +{ + SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_DAC_DIG_FORCE_M); + SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_DAC_CLK_INV_M); + + SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_DAC_XPD_FORCE_M); + SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC_M); + + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RUE_M); + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RDE_M); + + SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_DAC_XPD_FORCE_M); + SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC_M); + + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RUE_M); + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RDE_M); + return ESP_OK; +} + +esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (pin == NULL) { + return configure_dac_pin(); + } + if (pin->bck_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->bck_io_num)) { + ESP_LOGE(I2S_TAG, "bck_io_num error"); + return ESP_FAIL; + } + if (pin->ws_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->ws_io_num)) { + ESP_LOGE(I2S_TAG, "ws_io_num error"); + return ESP_FAIL; + } + if (pin->data_out_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_out_num)) { + ESP_LOGE(I2S_TAG, "data_out_num error"); + return ESP_FAIL; + } + if (pin->data_in_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_in_num)) { + ESP_LOGE(I2S_TAG, "data_in_num error"); + return ESP_FAIL; + } + + int bck_sig = -1, ws_sig = -1, data_out_sig = -1, data_in_sig = -1; + //TX & RX + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + bck_sig = I2S0I_BCK_OUT_IDX; + ws_sig = I2S0I_WS_OUT_IDX; + data_in_sig = I2S0I_DATA_IN15_IDX; + if (i2s_num == I2S_NUM_1) { + bck_sig = I2S1I_BCK_OUT_IDX; + ws_sig = I2S1I_WS_OUT_IDX; + data_in_sig = I2S1I_DATA_IN15_IDX; + } + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + bck_sig = I2S0O_BCK_OUT_IDX; + ws_sig = I2S0O_WS_OUT_IDX; + data_out_sig = I2S0O_DATA_OUT23_IDX; + if (i2s_num == I2S_NUM_1) { + bck_sig = I2S1O_BCK_OUT_IDX; + ws_sig = I2S1O_WS_OUT_IDX; + data_out_sig = I2S1O_DATA_OUT23_IDX; + } + } + + gpio_matrix_out_check(pin->data_out_num, data_out_sig, 0, 0); + gpio_matrix_in_check(pin->data_in_num, data_in_sig, 0); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { + gpio_matrix_out_check(pin->ws_io_num, ws_sig, 0, 0); + gpio_matrix_out_check(pin->bck_io_num, bck_sig, 0, 0); + } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { + gpio_matrix_in_check(pin->ws_io_num, ws_sig, 0); + gpio_matrix_in_check(pin->bck_io_num, bck_sig, 0); + } + ESP_LOGE(I2S_TAG, "data: out %d, in: %d, ws: %d, bck: %d", data_out_sig, data_in_sig, ws_sig, bck_sig); + return ESP_OK; +} + +esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_CHECK((p_i2s_obj[i2s_num]->bytes_per_sample > 0), "bits_per_sample not set", ESP_FAIL); + return i2s_set_clk(i2s_num, rate, p_i2s_obj[i2s_num]->bytes_per_sample*8, 0); +} +static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_config) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_CHECK((i2s_config), "param null", ESP_FAIL); + if (i2s_num == I2S_NUM_1) { + periph_module_enable(PERIPH_I2S1_MODULE); + } else { + periph_module_enable(PERIPH_I2S0_MODULE); + } + // configure I2S data port interface. + i2s_reset_fifo(i2s_num); + + //reset i2s + I2S[i2s_num]->conf.tx_reset = 1; + I2S[i2s_num]->conf.tx_reset = 0; + I2S[i2s_num]->conf.rx_reset = 1; + I2S[i2s_num]->conf.rx_reset = 0; + + + //reset dma + I2S[i2s_num]->lc_conf.in_rst = 1; + I2S[i2s_num]->lc_conf.in_rst = 0; + I2S[i2s_num]->lc_conf.out_rst = 1; + I2S[i2s_num]->lc_conf.out_rst = 0; + + + //Enable and configure DMA + I2S[i2s_num]->lc_conf.check_owner = 0; + I2S[i2s_num]->lc_conf.out_loop_test = 0; + I2S[i2s_num]->lc_conf.out_auto_wrback = 0; + I2S[i2s_num]->lc_conf.out_data_burst_en = 0; + I2S[i2s_num]->lc_conf.outdscr_burst_en = 0; + I2S[i2s_num]->lc_conf.out_no_restart_clr = 0; + I2S[i2s_num]->lc_conf.indscr_burst_en = 0; + I2S[i2s_num]->lc_conf.out_eof_mode = 1; + + + I2S[i2s_num]->conf2.lcd_en = 0; + I2S[i2s_num]->conf2.camera_en = 0; + I2S[i2s_num]->pdm_conf.pcm2pdm_conv_en = 0; + I2S[i2s_num]->pdm_conf.pdm2pcm_conv_en = 0; + + I2S[i2s_num]->fifo_conf.dscr_en = 0; + p_i2s_obj[i2s_num]->channel_num = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1; + + I2S[i2s_num]->conf_chan.tx_chan_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1); // 0-two channel;1-right;2-left;3-righ;4-left + I2S[i2s_num]->fifo_conf.tx_fifo_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + I2S[i2s_num]->conf.tx_mono = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + + I2S[i2s_num]->conf_chan.rx_chan_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1); // 0-two channel;1-right;2-left;3-righ;4-left + I2S[i2s_num]->fifo_conf.rx_fifo_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + I2S[i2s_num]->conf.rx_mono = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + I2S[i2s_num]->fifo_conf.dscr_en = 1;//connect dma to fifo + + I2S[i2s_num]->conf.tx_start = 0; + I2S[i2s_num]->conf.rx_start = 0; + + if (i2s_config->mode & I2S_MODE_TX) { + I2S[i2s_num]->conf.tx_msb_right = 0; + I2S[i2s_num]->conf.tx_right_first = 0; + + I2S[i2s_num]->conf.tx_slave_mod = 0; // Master + I2S[i2s_num]->fifo_conf.tx_fifo_mod_force_en = 1;//? + + if (i2s_config->mode & I2S_MODE_SLAVE) { + I2S[i2s_num]->conf.tx_slave_mod = 1;//TX Slave + } + } + + if (i2s_config->mode & I2S_MODE_RX) { + I2S[i2s_num]->conf.rx_msb_right = 0; + I2S[i2s_num]->conf.rx_right_first = 0; + I2S[i2s_num]->conf.rx_slave_mod = 0; // Master + I2S[i2s_num]->fifo_conf.rx_fifo_mod_force_en = 1;//? + I2S[i2s_num]->rx_eof_num = (i2s_config->dma_buf_len); + if (i2s_config->mode & I2S_MODE_SLAVE) { + I2S[i2s_num]->conf.rx_slave_mod = 1;//RX Slave + } + } + + if (i2s_config->mode & I2S_MODE_DAC_BUILT_IN) { + I2S[i2s_num]->conf2.lcd_en = 1; + I2S[i2s_num]->conf.tx_right_first = 1; + I2S[i2s_num]->fifo_conf.tx_fifo_mod = 3; + } + + if (i2s_config->communication_format & I2S_COMM_FORMAT_I2S) { + I2S[i2s_num]->conf.tx_short_sync = 0; + I2S[i2s_num]->conf.rx_short_sync = 0; + I2S[i2s_num]->conf.tx_msb_shift = 1; + I2S[i2s_num]->conf.rx_msb_shift = 1; + if (i2s_config->communication_format & I2S_COMM_FORMAT_I2S_LSB) { + if (i2s_config->mode & I2S_MODE_TX) { + I2S[i2s_num]->conf.tx_msb_shift = 0; + } + if (i2s_config->mode & I2S_MODE_RX) { + I2S[i2s_num]->conf.rx_msb_shift = 0; + } + } + } + + if (i2s_config->communication_format & I2S_COMM_FORMAT_PCM) { + I2S[i2s_num]->conf.tx_msb_shift = 0; + I2S[i2s_num]->conf.rx_msb_shift = 0; + I2S[i2s_num]->conf.tx_short_sync = 0; + I2S[i2s_num]->conf.rx_short_sync = 0; + if (i2s_config->communication_format & I2S_COMM_FORMAT_PCM_SHORT) { + if (i2s_config->mode & I2S_MODE_TX) { + I2S[i2s_num]->conf.tx_short_sync = 1; + } + if (i2s_config->mode & I2S_MODE_RX) { + I2S[i2s_num]->conf.rx_short_sync = 1; + } + } + } + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) { + I2S[i2s_num]->conf.sig_loopback = 1; + } + i2s_set_clk(i2s_num, i2s_config->sample_rate, p_i2s_obj[i2s_num]->bytes_per_sample*8, 0); + return ESP_OK; +} + +esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) +{ + int total_buffer_in_bytes = p_i2s_obj[i2s_num]->dma_buf_count * p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + const uint32_t zero_sample[2] = { 0 }; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + while (total_buffer_in_bytes > 0) { + i2s_push_sample(i2s_num, (const char*) zero_sample, 10); + total_buffer_in_bytes -= p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + } + return ESP_OK; +} + +esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_CHECK((i2s_config != NULL), "I2S configuration must not NULL", ESP_FAIL); + I2S_CHECK((i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), "I2S buffer count less than 128 and more than 2", ESP_FAIL); + I2S_CHECK((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 2048), "I2S buffer length at most 2048 and more than 8", ESP_FAIL); + if (p_i2s_obj[i2s_num] == NULL) { + p_i2s_obj[i2s_num] = (i2s_obj_t*) malloc(sizeof(i2s_obj_t)); + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGE(I2S_TAG, "Malloc I2S driver error"); + return ESP_FAIL; + } + + p_i2s_obj[i2s_num]->i2s_num = i2s_num; + p_i2s_obj[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; + p_i2s_obj[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; + p_i2s_obj[i2s_num]->i2s_queue = i2s_queue; + p_i2s_obj[i2s_num]->mode = i2s_config->mode; + p_i2s_obj[i2s_num]->bytes_per_sample = i2s_config->bits_per_sample/8; + + //initial dma + if (ESP_FAIL == i2s_isr_register(i2s_num, i2s_config->intr_alloc_flags, i2s_intr_handler_default, p_i2s_obj[i2s_num], &p_i2s_obj[i2s_num]->i2s_isr_handle)) { + free(p_i2s_obj[i2s_num]); + ESP_LOGE(I2S_TAG, "Register I2S Interrupt error"); + return ESP_FAIL; + } + i2s_stop(i2s_num); + i2s_param_config(i2s_num, i2s_config); + + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + p_i2s_obj[i2s_num]->tx = i2s_create_dma_queue(i2s_num, i2s_config->dma_buf_count, i2s_config->dma_buf_len); + if (p_i2s_obj[i2s_num]->tx == NULL) { + ESP_LOGE(I2S_TAG, "Failed to create tx dma buffer"); + i2s_driver_uninstall(i2s_num); + return ESP_FAIL; + } + I2S[i2s_num]->out_link.addr = (uint32_t) p_i2s_obj[i2s_num]->tx->desc[0]; + } + + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + p_i2s_obj[i2s_num]->rx = i2s_create_dma_queue(i2s_num, i2s_config->dma_buf_count, i2s_config->dma_buf_len); + if (p_i2s_obj[i2s_num]->rx == NULL){ + ESP_LOGE(I2S_TAG, "Failed to create rx dma buffer"); + i2s_driver_uninstall(i2s_num); + return ESP_FAIL; + } + I2S[i2s_num]->in_link.addr = (uint32_t) p_i2s_obj[i2s_num]->rx->desc[0]; + } + + + if (i2s_queue) { + p_i2s_obj[i2s_num]->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); + *((QueueHandle_t*) i2s_queue) = p_i2s_obj[i2s_num]->i2s_queue; + ESP_LOGI(I2S_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_i2s_obj[i2s_num]->i2s_queue)); + } else { + p_i2s_obj[i2s_num]->i2s_queue = NULL; + } + + i2s_start(i2s_num); + } else { + ESP_LOGE(I2S_TAG, "I2S driver already installed"); + return ESP_FAIL; + } + + return ESP_OK; +} + +esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGI(I2S_TAG, "ALREADY NULL"); + return ESP_OK; + } + i2s_stop(i2s_num); + esp_intr_free(p_i2s_obj[i2s_num]->i2s_isr_handle); + + if (p_i2s_obj[i2s_num]->tx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->tx); + p_i2s_obj[i2s_num]->tx = NULL; + } + if (p_i2s_obj[i2s_num]->rx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->rx); + p_i2s_obj[i2s_num]->rx = NULL; + } + + if (p_i2s_obj[i2s_num]->i2s_queue) { + vQueueDelete(p_i2s_obj[i2s_num]->i2s_queue); + p_i2s_obj[i2s_num]->i2s_queue = NULL; + } + + free(p_i2s_obj[i2s_num]); + p_i2s_obj[i2s_num] = NULL; + + if (i2s_num == I2S_NUM_0) { + periph_module_disable(PERIPH_I2S0_MODULE); + } else if (i2s_num == I2S_NUM_1) { + periph_module_disable(PERIPH_I2S1_MODULE); + } + return ESP_OK; +} + +int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t ticks_to_wait) +{ + char *data_ptr; + int bytes_can_write, bytes_writen = 0; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->tx == NULL) { + return 0; + } + xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + while (size > 0) { + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + break; + } + p_i2s_obj[i2s_num]->tx->rw_pos = 0; + } + ESP_LOGD(I2S_TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", size, p_i2s_obj[i2s_num]->tx->rw_pos, p_i2s_obj[i2s_num]->tx->buf_size, (int)p_i2s_obj[i2s_num]->tx->curr_ptr); + data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + bytes_can_write = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; + if (bytes_can_write > size) { + bytes_can_write = size; + } + memcpy(data_ptr, src, bytes_can_write); + size -= bytes_can_write; + src += bytes_can_write; + p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + bytes_writen += bytes_can_write; + } + xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); + return bytes_writen; +} + +int i2s_read_bytes(i2s_port_t i2s_num, char* dest, size_t size, TickType_t ticks_to_wait) +{ + char *data_ptr; + int bytes_can_read, byte_read = 0; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->rx == NULL) { + return 0; + } + xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + while (size > 0) { + if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->rx->queue, &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) == pdFALSE) { + break; + } + p_i2s_obj[i2s_num]->rx->rw_pos = 0; + } + data_ptr = (char*)p_i2s_obj[i2s_num]->rx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; + bytes_can_read = p_i2s_obj[i2s_num]->rx->buf_size - p_i2s_obj[i2s_num]->rx->rw_pos; + if (bytes_can_read > size) { + bytes_can_read = size; + } + memcpy(dest, data_ptr, bytes_can_read); + size -= bytes_can_read; + dest += bytes_can_read; + p_i2s_obj[i2s_num]->rx->rw_pos += bytes_can_read; + byte_read += bytes_can_read; + } + xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux); + return byte_read; +} +int i2s_push_sample(i2s_port_t i2s_num, const char *sample, TickType_t ticks_to_wait) +{ + int i, bytes_to_push = 0; + char *data_ptr; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + return 0; + } + ESP_LOGD(I2S_TAG, "rw_pos: %d, buf_size: %d, curr_ptr: %d", p_i2s_obj[i2s_num]->tx->rw_pos, p_i2s_obj[i2s_num]->tx->buf_size, (int)p_i2s_obj[i2s_num]->tx->curr_ptr); + p_i2s_obj[i2s_num]->tx->rw_pos = 0; + } + data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *data_ptr++ = *sample++; + bytes_to_push ++; + } + if (p_i2s_obj[i2s_num]->channel_num == 2) { + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *data_ptr++ = *sample++; + bytes_to_push ++; + } + } + + p_i2s_obj[i2s_num]->tx->rw_pos += p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + return bytes_to_push; +} + +int i2s_pop_sample(i2s_port_t i2s_num, char *sample, TickType_t ticks_to_wait) +{ + int i, bytes_to_pop = 0; + char *data_ptr; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->rx->queue, &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) == pdFALSE) { + return 0; + } + p_i2s_obj[i2s_num]->rx->rw_pos = 0; + } + data_ptr = (char*)p_i2s_obj[i2s_num]->rx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *sample++ = *data_ptr++; + bytes_to_pop++; + } + if (p_i2s_obj[i2s_num]->channel_num == 2) { + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *sample++ = *data_ptr++; + bytes_to_pop++; + } + } + + p_i2s_obj[i2s_num]->rx->rw_pos += p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + return bytes_to_pop; +} diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 83d3806834..1472ba8352 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -156,6 +156,7 @@ typedef enum { GPIO_NUM_37 = 37, /*!< GPIO37, input mode only */ GPIO_NUM_38 = 38, /*!< GPIO38, input mode only */ GPIO_NUM_39 = 39, /*!< GPIO39, input mode only */ + GPIO_NUM_MAX = 40, } gpio_num_t; typedef enum { @@ -205,9 +206,8 @@ typedef enum { } gpio_pull_mode_t; - +typedef void (*gpio_isr_t)(void*); typedef intr_handle_t gpio_isr_handle_t; -typedef void (*gpio_event_callback)(gpio_num_t gpio_intr_num); /** * @brief GPIO common configuration @@ -269,7 +269,7 @@ esp_err_t gpio_intr_disable(gpio_num_t gpio_num); * * @return * - ESP_OK Success - * - GPIO_IS_VALID_GPIO GPIO number error + * - ESP_ERR_INVALID_ARG GPIO number error * */ esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level); @@ -357,8 +357,6 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); */ esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle); - - /** * @brief Enable pull-up on GPIO. * @@ -403,93 +401,55 @@ esp_err_t gpio_pulldown_en(gpio_num_t gpio_num); */ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); +/** + * @brief Install a GPIO ISR service, so we can assign different ISR handler for different pins + * + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * + * @return + * - ESP_OK Success + * - ESP_FAIL Operation fail + * - ESP_ERR_NO_MEM No memory to install this service + */ +esp_err_t gpio_install_isr_service(int intr_alloc_flags); /** - * *************** ATTENTION ********************/ -/** - *@attention - * Each GPIO has its own separate configuration register, so we do not use - * a lock to serialize access to them. This works under the assumption that - * no situation will occur where two tasks try to configure the same GPIO - * pin simultaneously. It is up to the application developer to guarantee this. - */ + * @brief Un-install GPIO ISR service, free the resources. + */ +void gpio_uninstall_isr_service(); /** - *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * - * @code{c} - * gpio_config_t io_conf; - * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt - * io_conf.mode = GPIO_MODE_OUTPUT; //set as output mode - * io_conf.pin_bit_mask = GPIO_SEL_18 | GPIO_SEL_19; //bit mask of the pins that you want to set,e.g.GPIO18/19 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 0; //disable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * @endcode - **/ + * @brief Add ISR handler for the corresponding GPIO. + * + * Interrupt handlers no longer need to be declared with IRAM_ATTR, unless you pass the ESP_INTR_FLAG_IRAM flag + * when allocating the ISR in gpio_install_isr_service(). + * This ISR handler will be called from an ISR. So there probably is some stack size limit, and this limit + * is smaller compared to a "raw" interrupt handler due to another level of indirection. + * + * @param gpio_num GPIO number + * @param isr_handler ISR handler function for the corresponding GPIO number. + * @param args parameter for ISR handler. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Wrong state, the ISR service has not been initialized. + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void* args); /** - *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * - * @code{c} - * io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt - * io_conf.mode = GPIO_MODE_INPUT; //set as input - * io_conf.pin_bit_mask = GPIO_SEL_4 | GPIO_SEL_5; //bit mask of the pins that you want to set, e.g.,GPIO4/5 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 1; //enable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * @endcode - */ -/** - *----------EXAMPLE TO SET ISR HANDLER ---------------------- - * @code{c} - * gpio_isr_register(gpio_intr_test, 0, NULL); //hook the isr handler for GPIO interrupt - * @endcode - */ -/** - *-------------EXAMPLE OF HANDLER FUNCTION-------------------* - * @code{c} - * #include "esp_attr.h" - * void IRAM_ATTR gpio_intr_test(void* arg) - * { - * //GPIO intr process - * ets_printf("in gpio_intr\n"); - * uint32_t gpio_num = 0; - * uint32_t gpio_intr_status = READ_PERI_REG(GPIO_STATUS_REG); //read status to get interrupt status for GPIO0-31 - * uint32_t gpio_intr_status_h = READ_PERI_REG(GPIO_STATUS1_REG);//read status1 to get interrupt status for GPIO32-39 - * SET_PERI_REG_MASK(GPIO_STATUS_W1TC_REG, gpio_intr_status); //Clear intr for gpio0-gpio31 - * SET_PERI_REG_MASK(GPIO_STATUS1_W1TC_REG, gpio_intr_status_h); //Clear intr for gpio32-39 - * do { - * if(gpio_num < 32) { - * if(gpio_intr_status & BIT(gpio_num)) { //gpio0-gpio31 - * ets_printf("Intr GPIO%d ,val: %d\n",gpio_num,gpio_get_level(gpio_num)); - * //This is an isr handler, you should post an event to process it in RTOS queue. - * } - * } else { - * if(gpio_intr_status_h & BIT(gpio_num - 32)) { - * ets_printf("Intr GPIO%d, val : %d\n",gpio_num,gpio_get_level(gpio_num)); - * //This is an isr handler, you should post an event to process it in RTOS queue. - * } - * } - * } while(++gpio_num < GPIO_PIN_COUNT); - * } - * @endcode - */ + * @brief Remove ISR handler for the corresponding GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Wrong state, the ISR service has not been initialized. + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_isr_handler_remove(gpio_num_t gpio_num); -/** - *----EXAMPLE OF I2C CONFIG AND PICK SIGNAL FOR IO MATRIX---* - * @code{c} - * gpio_config_t io_conf; - * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt - * io_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; //set as output mode - * io_conf.pin_bit_mask = GPIO_SEL_21 | GPIO_SEL_22; //bit mask of the pins that you want to set,e.g.GPIO21/22 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 1; //enable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * gpio_matrix_out(21, EXT_I2C_SCL_O_IDX, 0, 0); //set output signal for io_matrix - * gpio_matrix_out(22, EXT_I2C_SDA_O_IDX, 0, 0); //set output signal for io_matrix - * gpio_matrix_in( 22, EXT_I2C_SDA_I_IDX, 0); //set input signal for io_matrix - * @endcode - * - */ #ifdef __cplusplus } diff --git a/components/driver/include/driver/i2c.h b/components/driver/include/driver/i2c.h new file mode 100644 index 0000000000..960ec61d27 --- /dev/null +++ b/components/driver/include/driver/i2c.h @@ -0,0 +1,514 @@ +// Copyright 2015-2016 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 _DRIVER_I2C_H_ +#define _DRIVER_I2C_H_ + + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/ringbuf.h" +#include "driver/gpio.h" + +#define I2C_APB_CLK_FREQ APB_CLK_FREQ /*!< I2C source clock is APB clock, 80MHz */ +#define I2C_FIFO_LEN (32) /*!< I2C hardware fifo length */ +typedef enum{ + I2C_MODE_SLAVE = 0, /*!< I2C slave mode */ + I2C_MODE_MASTER, /*!< I2C master mode */ + I2C_MODE_MAX, +}i2c_mode_t; + +typedef enum { + I2C_MASTER_WRITE = 0, /*!< I2C write data */ + I2C_MASTER_READ, /*!< I2C read data */ +} i2c_rw_t; + +typedef enum { + I2C_DATA_MODE_MSB_FIRST = 0, /*!< I2C data msb first */ + I2C_DATA_MODE_LSB_FIRST = 1, /*!< I2C data lsb first */ + I2C_DATA_MODE_MAX +} i2c_trans_mode_t; + +typedef enum{ + I2C_CMD_RESTART = 0, /*!=0) The number of data bytes that pushed to the I2C slave buffer. + */ +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE_TYPE ticks_to_wait); + +/** + * @brief I2C slave read data from internal buffer. When I2C slave receive data, isr will copy received data + * from hardware rx fifo to internal ringbuffer. Then users can read from internal ringbuffer. + * @note + * Only call this function in I2C slave mode + * + * @param i2c_num I2C port number + * @param data data pointer to write into internal buffer + * @param max_size Maximum data size to read + * @param ticks_to_wait Maximum waiting ticks + * + * @return + * - ESP_FAIL(-1) Parameter error + * - Others(>=0) The number of data bytes that read from I2C slave buffer. + */ +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait); + +/** + * @brief set I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period clock cycle number during SCL is high level, high_period is a 14 bit value + * @param low_period clock cycle number during SCL is low level, low_period is a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period); + +/** + * @brief get I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period pointer to get clock cycle number during SCL is high level, will get a 14 bit value + * @param low_period pointer to get clock cycle number during SCL is low level, will get a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_period(i2c_port_t i2c_num, int* high_period, int* low_period); + +/** + * @brief set I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock number between the falling-edge of SDA and rising-edge of SCL for start mark, it's a 10-bit value. + * @param hold_time clock num between the falling-edge of SDA and falling-edge of SCL for start mark, it's a 10-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock num between the rising-edge of SCL and the rising-edge of SDA, it's a 10-bit value. + * @param hold_time clock number after the STOP bit's rising-edge, it's a 14-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time. + * @param hold_time pointer to get hold time. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time clock number I2C used to sample data on SDA after the rising-edge of SCL, it's a 10-bit value + * @param hold_time clock number I2C used to hold the data after the falling-edge of SCL, it's a 10-bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time); + +/** + * @brief get I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time pointer to get sample time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time); + +/** + * @brief set I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode I2C sending data mode + * @param rx_trans_mode I2C receving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode); + +/** + * @brief get I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode pointer to get I2C sending data mode + * @param rx_trans_mode pointer to get I2C receiving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode); + +#ifdef __cplusplus +} +#endif + +#endif /*_DRIVER_I2C_H_*/ diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h new file mode 100644 index 0000000000..d44bb676a3 --- /dev/null +++ b/components/driver/include/driver/i2s.h @@ -0,0 +1,380 @@ +// Copyright 2015-2016 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 _DRIVER_I2S_H_ +#define _DRIVER_I2S_H_ +#include "esp_err.h" +#include +#include "soc/gpio_reg.h" +#include "soc/soc.h" +#include "soc/i2s_struct.h" +#include "soc/i2s_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/io_mux_reg.h" +#include "rom/gpio.h" +#include "esp_attr.h" +#include "esp_intr_alloc.h" +#include "driver/periph_ctrl.h" +#include "freertos/semphr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define I2S_PIN_NO_CHANGE (-1) + + +/** + * @brief I2S bit width per sample. + * + */ +typedef enum { + I2S_BITS_PER_SAMPLE_8BIT = 8, /*!< I2S bits per sample: 8-bits*/ + I2S_BITS_PER_SAMPLE_16BIT = 16, /*!< I2S bits per sample: 16-bits*/ + I2S_BITS_PER_SAMPLE_24BIT = 24, /*!< I2S bits per sample: 24-bits*/ + I2S_BITS_PER_SAMPLE_32BIT = 32, /*!< I2S bits per sample: 32-bits*/ +} i2s_bits_per_sample_t; + +/** + * @brief I2S communication standard format + * + */ +typedef enum { + I2S_COMM_FORMAT_I2S = 0x01, /*!< I2S communication format I2S*/ + I2S_COMM_FORMAT_I2S_MSB = 0x02, /*!< I2S format MSB*/ + I2S_COMM_FORMAT_I2S_LSB = 0x04, /*!< I2S format LSB*/ + I2S_COMM_FORMAT_PCM = 0x08, /*!< I2S communication format PCM*/ + I2S_COMM_FORMAT_PCM_SHORT = 0x10, /*!< PCM Short*/ + I2S_COMM_FORMAT_PCM_LONG = 0x20, /*!< PCM Long*/ +} i2s_comm_format_t; + + +/** + * @brief I2S channel format type + */ +typedef enum { + I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00, + I2S_CHANNEL_FMT_ALL_RIGHT, + I2S_CHANNEL_FMT_ALL_LEFT, + I2S_CHANNEL_FMT_ONLY_RIGHT, + I2S_CHANNEL_FMT_ONLY_LEFT, +} i2s_channel_fmt_t; + +/** + * @brief PDM sample rate ratio, measured in Hz. + * + */ +typedef enum { + PDM_SAMPLE_RATE_RATIO_64, + PDM_SAMPLE_RATE_RATIO_128, +} pdm_sample_rate_ratio_t; + +/** + * @brief PDM PCM convter enable/disable. + * + */ +typedef enum { + PDM_PCM_CONV_ENABLE, + PDM_PCM_CONV_DISABLE, +} pdm_pcm_conv_t; + + +/** + * @brief I2S Peripheral, 0 & 1. + * + */ +typedef enum { + I2S_NUM_0 = 0x0, /*!< I2S 0*/ + I2S_NUM_1 = 0x1, /*!< I2S 1*/ + I2S_NUM_MAX, +} i2s_port_t; + +/** + * @brief I2S Mode, defaut is I2S_MODE_MASTER | I2S_MODE_TX + * + */ +typedef enum { + I2S_MODE_MASTER = 1, + I2S_MODE_SLAVE = 2, + I2S_MODE_TX = 4, + I2S_MODE_RX = 8, + I2S_MODE_DAC_BUILT_IN = 16 +} i2s_mode_t; + +/** + * @brief I2S configuration parameters for i2s_param_config function + * + */ +typedef struct { + i2s_mode_t mode; /*!< I2S work mode*/ + int sample_rate; /*!< I2S sample rate*/ + i2s_bits_per_sample_t bits_per_sample; /*!< I2S bits per sample*/ + i2s_channel_fmt_t channel_format; /*!< I2S channel format */ + i2s_comm_format_t communication_format; /*!< I2S communication format */ + int intr_alloc_flags; /*!< Flags used to allocate the interrupt. One or multiple (ORred) ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info */ + int dma_buf_count; /*!< I2S DMA Buffer Count */ + int dma_buf_len; /*!< I2S DMA Buffer Length */ +} i2s_config_t; + +/** + * @brief I2S event types + * + */ +typedef enum { + I2S_EVENT_DMA_ERROR, + I2S_EVENT_TX_DONE, /*!< I2S DMA finish sent 1 buffer*/ + I2S_EVENT_RX_DONE, /*!< I2S DMA finish received 1 buffer*/ + I2S_EVENT_MAX, /*!< I2S event max index*/ +} i2s_event_type_t; + +/** + * @brief Event structure used in I2S event queue + * + */ +typedef struct { + i2s_event_type_t type; /*!< I2S event type */ + size_t size; /*!< I2S data size for I2S_DATA event*/ +} i2s_event_t; + +/** + * @brief I2S pin number for i2s_set_pin + * + */ +typedef struct { + int bck_io_num; /*!< BCK in out pin*/ + int ws_io_num; /*!< WS in out pin*/ + int data_out_num; /*!< DATA out pin*/ + int data_in_num; /*!< DATA in pin*/ +} i2s_pin_config_t; + +typedef intr_handle_t i2s_isr_handle_t; +/** + * @brief Set I2S pin number + * + * @note + * Internal signal can be output to multiple GPIO pads + * Only one GPIO pad can connect with input signal + * + * @param i2s_num I2S_NUM_0 or I2S_NUM_1 + * + * @param pin I2S Pin struct, or NULL for 2-channels, 8-bits DAC pin configuration (GPIO25 & GPIO26) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin); + +/** + * @brief i2s install and start driver + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param i2s_config I2S configurations - see i2s_config_t struct + * + * @param queue_size I2S event queue size/depth. + * + * @param i2s_queue I2S event queue handle, if set NULL, driver will not use an event queue. + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue); + +/** + * @brief Uninstall I2S driver. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num); + +/** + * @brief i2s read data buffer to i2s dma buffer + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param src source address to write + * + * @param size size of data (size in bytes) + * + * @param ticks_to_wait Write timeout + * + * @return number of written bytes + */ +int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t ticks_to_wait); + +/** + * @brief i2s write data buffer to i2s dma buffer + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param dest destination address to read + * + * @param size size of data (size in bytes) + * + * @param ticks_to_wait Read timeout + * + * @return number of read bytes + */ +int i2s_read_bytes(i2s_port_t i2s_num, char* dest, size_t size, TickType_t ticks_to_wait); + +/** + * @brief i2s push 1 sample to i2s dma buffer, with the size parameter equal to one sample's size in bytes = bits_per_sample/8. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param sample destination address to write (depend on bits_per_sample, size of sample (in bytes) = 2*bits_per_sample/8) + * + * @param ticks_to_wait Push timeout + * + * @return number of push bytes + */ +int i2s_push_sample(i2s_port_t i2s_num, const char *sample, TickType_t ticks_to_wait); + +/** + * @brief Pop 1 sample to i2s dma buffer, with the size parameter equal to one sample's size in bytes = bits_per_sample/8. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param sample destination address to write (depend on bits_per_sample, size of sample (in bytes) = 2*bits_per_sample/8) + * + * @param ticks_to_wait Pop timeout + * + * @return number of pop bytes + */ +int i2s_pop_sample(i2s_port_t i2s_num, char *sample, TickType_t ticks_to_wait); + + +/** + * @brief Set clock rate used for I2S RX and TX + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param rate I2S clock (ex: 8000, 44100...) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate); + +/** + * @brief Start driver + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * +* @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_start(i2s_port_t i2s_num); + +/** + * @brief Stop driver + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_stop(i2s_port_t i2s_num); + +/** + * @brief Set the TX DMA buffer contents to all zeroes + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num); + +/***************************EXAMPLE********************************** + * + * + * ----------------EXAMPLE OF I2S SETTING --------------------- + * @code{c} + * + * #include "freertos/queue.h" + * #define I2S_INTR_NUM 17 //choose one interrupt number from soc.h + * int i2s_num = 0; //i2s port number + * i2s_config_t i2s_config = { + * .mode = I2S_MODE_MASTER | I2S_MODE_TX, + * .sample_rate = 44100, + * .bits_per_sample = 16, //16, 32 + * .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //format LEFT_RIGHT + * .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, + * .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + * .dma_buf_count = 8, + * .dma_buf_len = 64 + * }; + * + * i2s_pin_config_t pin_config = { + * .bck_io_num = 26, + * .ws_io_num = 25, + * .data_out_num = 22, + * .data_in_num = I2S_PIN_NO_CHANGE + * }; + * + * i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + * + * i2s_set_pin(i2s_num, &pin_config); + * + * i2s_set_sample_rates(i2s_num, 22050); //set sample rates + * + * + * i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver + *@endcode + * + * ----------------EXAMPLE USING I2S WITH DAC --------------------- + * @code{c} + * + * #include "freertos/queue.h" + * #define I2S_INTR_NUM 17 //choose one interrupt number from soc.h + * int i2s_num = 0; //i2s port number + * i2s_config_t i2s_config = { + * .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, + * .sample_rate = 44100, + * .bits_per_sample = 8, // Only 8-bit DAC support + * .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // + * .communication_format = I2S_COMM_FORMAT_I2S_MSB, + * .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + * .dma_buf_count = 8, + * .dma_buf_len = 64 + * }; + * + * + * i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + * + * i2s_set_pin(i2s_num, NULL); //for internal DAC + * + * i2s_set_sample_rates(i2s_num, 22050); //set sample rates + * + * i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver + *@endcode + *-----------------------------------------------------------------------------* + ***************************END OF EXAMPLE**********************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_I2S_H_ */ diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index fb97c6c011..af7c6b8078 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -16,9 +16,6 @@ #define _DRIVER_LEDC_H_ #include "esp_err.h" #include "soc/soc.h" -#include "soc/ledc_reg.h" -#include "soc/ledc_reg.h" -#include "soc/ledc_struct.h" #include "driver/gpio.h" #include "driver/periph_ctrl.h" #include "esp_intr_alloc.h" @@ -68,6 +65,7 @@ typedef enum { LEDC_CHANNEL_5, /*!< LEDC channel 5 */ LEDC_CHANNEL_6, /*!< LEDC channel 6 */ LEDC_CHANNEL_7, /*!< LEDC channel 7 */ + LEDC_CHANNEL_MAX, } ledc_channel_t; typedef enum { @@ -79,6 +77,11 @@ typedef enum { LEDC_TIMER_15_BIT = 15, /*!< LEDC PWM depth 15Bit */ } ledc_timer_bit_t; +typedef enum { + LEDC_FADE_NO_WAIT = 0, /*!< LEDC fade function will return immediately */ + LEDC_FADE_WAIT_DONE, /*!< LEDC fade function will block until fading to the target duty*/ + LEDC_FADE_MAX, +} ledc_fade_mode_t; /** * @brief Configuration parameters of LEDC channel for ledc_channel_config function */ @@ -104,43 +107,39 @@ typedef struct { typedef intr_handle_t ledc_isr_handle_t; /** - * @brief LEDC channel configuration + * @brief LEDC channel configuration + * Configure LEDC channel with the given channel/output gpio_num/interrupt/source timer/frequency(Hz)/LEDC depth * - * User this Function, configure LEDC channel with the given channel/output gpio_num/interrupt/source timer/frequency(Hz)/LEDC depth + * @param ledc_conf Pointer of LEDC channel configure struct * - * @param ledc_conf Pointer of LEDC channel configure struct * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error - * */ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf); /** - * @brief LEDC timer configuration - * - * User this Function, configure LEDC timer with the given source timer/frequency(Hz)/bit_num + * @brief LEDC timer configuration + * Configure LEDC timer with the given source timer/frequency(Hz)/bit_num * * @param timer_conf Pointer of LEDC timer configure struct * - * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error * - ESP_FAIL Can not find a proper pre-divider number base on the given frequency and the current bit_num. - * */ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf); /** - * @brief LEDC update channel parameters + * @brief LEDC update channel parameters + * Call this function to activate the LEDC updated parameters. + * After ledc_set_duty, ledc_set_fade, we need to call this function to update the settings. * - * Call this function to activate the LEDC updated parameters. - * After ledc_set_duty, ledc_set_fade, we need to call this function to update the settings. - * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param channel LEDC channel(0-7), select from ledc_channel_t + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, + * now we only support high-speed mode. + * We will access low-speed mode in next version + * @param channel LEDC channel(0-7), select from ledc_channel_t * * @return * - ESP_OK Success @@ -150,14 +149,11 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf); esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); /** - * @brief LEDC stop - * - * Disable LEDC output, and set idle level + * @brief LEDC stop. + * Disable LEDC output, and set idle level * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t - * * @param idle_level Set output idle level after LEDC stops. * * @return @@ -167,14 +163,10 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level); /** - * @brief LEDC set channel frequency(Hz) - * - * Set LEDC frequency(Hz) + * @brief LEDC set channel frequency(Hz) * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_num LEDC timer index(0-3), select from ledc_timer_t - * * @param freq_hz Set the LEDC frequency * * @return @@ -188,25 +180,20 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t * @brief LEDC get channel frequency(Hz) * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_num LEDC timer index(0-3), select from ledc_timer_t * * @return * - 0 error * - Others Current LEDC frequency - * */ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num); /** - * @brief LEDC set duty - * - * Set LEDC duty, After the function calls the ledc_update_duty function, the function can take effect. + * @brief LEDC set duty + * Only after calling ledc_update_duty will the duty update. * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t - * * @param duty Set the LEDC duty, the duty range is [0, (2**bit_num) - 1] * * @return @@ -216,57 +203,47 @@ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num); esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty); /** - * @brief LEDC get duty + * @brief LEDC get duty * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t * - * * @return * - (-1) parameter error * - Others Current LEDC duty - * */ int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel); /** - * @brief LEDC set gradient - * - * Set LEDC gradient, After the function calls the ledc_update_duty function, the function can take effect. + * @brief LEDC set gradient + * Set LEDC gradient, After the function calls the ledc_update_duty function, the function can take effect. * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t - * * @param duty Set the start of the gradient duty, the duty range is [0, (2**bit_num) - 1] - * * @param gradule_direction Set the direction of the gradient - * * @param step_num Set the number of the gradient - * * @param duty_cyle_num Set how many LEDC tick each time the gradient lasts - * * @param duty_scale Set gradient change amplitude * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ -esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t gradule_direction, +esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty, ledc_duty_direction_t gradule_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale); /** - * @brief register LEDC interrupt handler, the handler is an ISR. - * The handler will be attached to the same CPU core that this function is running on. + * @brief Register LEDC interrupt handler, the handler is an ISR. + * The handler will be attached to the same CPU core that this function is running on. * - * @param fn Interrupt handler function. - * @param arg User-supplied argument passed to the handler function. - * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) - * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. - * @param arg Parameter for handler function - * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will - * be returned here. + * @param fn Interrupt handler function. + * @param arg User-supplied argument passed to the handler function. + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * @param arg Parameter for handler function + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success @@ -275,48 +252,38 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, ledc_isr_handle_t *handle); /** - * @brief configure LEDC settings + * @brief Configure LEDC settings * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param timer_sel Timer index(0-3), there are 4 timers in LEDC module - * - * @param div_num Timer clock divide number, the timer clock is divided from the selected clock source - * - * @param bit_num The count number of one period, counter range is 0 ~ ((2 ** bit_num) - 1) - * - * @param clk_src Select LEDC source clock. + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param timer_sel Timer index(0-3), there are 4 timers in LEDC module + * @param div_num Timer clock divide number, the timer clock is divided from the selected clock source + * @param bit_num The count number of one period, counter range is 0 ~ ((2 ** bit_num) - 1) + * @param clk_src Select LEDC source clock. * * @return * - (-1) Parameter error * - Other Current LEDC duty - * */ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src); /** - * @brief reset LEDC timer + * @brief Reset LEDC timer * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * - * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success - * */ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel); /** - * @brief pause LEDC timer counter + * @brief Pause LEDC timer counter * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * - * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success @@ -325,104 +292,96 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel); esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel); /** - * @brief pause LEDC timer resume - * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t + * @brief Resume LEDC timer * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success - * */ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel); /** - * @brief bind LEDC channel with the selected timer - * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param channel LEDC channel index(0-7), select from ledc_channel_t - * - * @param timer_idx LEDC timer index(0-3), select from ledc_timer_t + * @brief Bind LEDC channel with the selected timer * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param channel LEDC channel index(0-7), select from ledc_channel_t + * @param timer_idx LEDC timer index(0-3), select from ledc_timer_t * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success - * */ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx); -/***************************EXAMPLE********************************** +/** + * @brief Set LEDC fade function. Should call ledc_fade_func_install() before calling this function. + * Call ledc_fade_start() after this to start fading. * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, + * For now we only support high-speed mode. We will access low-speed mode soon. + * @param channel LEDC channel index(0-7), select from ledc_channel_t + * @param target_duty Target duty of fading.( 0 - (2 ** bit_num - 1))) + * @param scale Controls the increase or decrease step scale. + * @param cycle_num increase or decrease the duty every cycle_num cycles * - * ----------------EXAMPLE OF LEDC SETTING --------------------- - * @code{c} - * //1. enable LEDC - * //enable LEDC module, or you can not set any register of it. - * periph_module_enable(PERIPH_LEDC_MODULE); - * @endcode - * - * @code{c} - * //2. set LEDC timer - * ledc_timer_config_t timer_conf = { - * .bit_num = LEDC_TIMER_12_BIT, //set timer counter bit number - * .freq_hz = 1000, //set frequency of pwm, here, 1000Hz - * .speed_mode = LEDC_HIGH_SPEED_MODE, //timer mode, - * .timer_num = LEDC_TIMER_0, //timer number - * }; - * ledc_timer_config(&timer_conf); //setup timer. - * @endcode - * - * @code{c} - * //3. set LEDC channel - * ledc_channel_config_t ledc_conf = { - * .channel = LEDC_CHANNEL_0; //set LEDC channel 0 - * .duty = 1000; //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) - * .gpio_num = 16; //GPIO number - * .intr_type = LEDC_INTR_FADE_END; //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. - * .speed_mode = LEDC_HIGH_SPEED_MODE; //set LEDC mode, from ledc_mode_t - * .timer_sel = LEDC_TIMER_0; //set LEDC timer source, if different channel use one timer, the frequency and bit_num of these channels should be the same - * } - * ledc_channel_config(&ledc_conf); //setup the configuration - * - * ----------------EXAMPLE OF SETTING DUTY --- ----------------- - * @code{c} - * uint32_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) - * uint32_t duty = 2000; //duty range is 0 ~ ((2**bit_num)-1) - * LEDC_set_duty(LEDC_HIGH_SPEED_MODE, ledc_channel, duty); //set speed mode, channel, and duty. - * ledc_update_duty(LEDC_HIGH_SPEED_MODE, ledc_channel); //after set duty, we need to call ledc_update_duty to update the settings. - * @endcode - * - * ----------------EXAMPLE OF LEDC INTERRUPT ------------------ - * @code{c} - * //we have fade_end interrupt and counter overflow interrupt. we just give an example of fade_end interrupt here. - * ledc_isr_register(ledc_isr_handler, NULL, 0); //hook the isr handler for LEDC interrupt - * @endcode - * - * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- - * @code{c} - * #include "esp_attr.h" - * void IRAM_ATTR ledc_isr_handler(void* arg) //we should add 'IRAM_ATTR' attribution when we declare the isr function - * { - * uint32_t intr_st = LEDC.int_st.val; //read LEDC interrupt status. - * - * //you will find which channels have triggered fade_end interrupt here, - * //then, you can post some event to RTOS queue to process the event. - * //later we will add a queue in the driver code. - * - * LEDC.int_clr.val = intr_st; //clear LEDC interrupt status. - * } - * @endcode - * - *--------------------------END OF EXAMPLE -------------------------- + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function not installed. */ +esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int scale, int cycle_num); +/** + * @brief Set LEDC fade function, with a limited time. Should call ledc_fade_func_install() before calling this function. + * Call ledc_fade_start() after this to start fading. + * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, + * For now we only support high-speed mode. We will access low-speed mode soon. + * @param channel LEDC channel index(0-7), select from ledc_channel_t + * @param target_duty Target duty of fading.( 0 - (2 ** bit_num - 1))) + * @param max_fade_time_ms The maximum time of the fading ( ms ). + * + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function not installed. + */ +esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int max_fade_time_ms); +/** + * @brief Install ledc fade function. This function will occupy interrupt of LEDC module. + * + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * + * @return + * - ESP_ERR_NO_MEM No enough memory + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function already installed. + */ +esp_err_t ledc_fade_func_install(int intr_alloc_flags); +/** + * @brief Uninstall LEDC fade function. + * + */ +void ledc_fade_func_uninstall(); + +/** + * @brief Start LEDC fading. + * + * @param channel LEDC channel number + * @param wait_done Whether to block until fading done. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function not installed. + * - ESP_ERR_INVALID_ARG Parameter error. + */ +esp_err_t ledc_fade_start(ledc_channel_t channel, ledc_fade_mode_t wait_done); #ifdef __cplusplus } diff --git a/components/driver/include/driver/periph_ctrl.h b/components/driver/include/driver/periph_ctrl.h index 8c404e5b13..0aab55088d 100644 --- a/components/driver/include/driver/periph_ctrl.h +++ b/components/driver/include/driver/periph_ctrl.h @@ -41,6 +41,9 @@ typedef enum { PERIPH_UHCI1_MODULE, PERIPH_RMT_MODULE, PERIPH_PCNT_MODULE, + PERIPH_SPI_MODULE, + PERIPH_HSPI_MODULE, + PERIPH_VSPI_MODULE, } periph_module_t; /** diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 24df1ac8ed..36e33e732e 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -524,7 +524,9 @@ esp_err_t rmt_set_err_intr_en(rmt_channel_t channel, bool en); esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en); /** - * @brief Set RMT TX event interrupt enable + * @brief Set RMT TX threshold event interrupt enable + * + * Causes an interrupt when a threshold number of items have been transmitted. * * @param channel RMT channel (0 - 7) * @@ -536,7 +538,7 @@ esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en); * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success */ -esp_err_t rmt_set_evt_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh); +esp_err_t rmt_set_tx_thr_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh); /** * @brief Set RMT pins diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h new file mode 100644 index 0000000000..9913bfd175 --- /dev/null +++ b/components/driver/include/driver/sdmmc_defs.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2006 Uwe Stuehler + * Adaptations to ESP-IDF Copyright (c) 2016 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. + */ + +#ifndef _SDMMC_DEFS_H_ +#define _SDMMC_DEFS_H_ + +#include +#include + +/* MMC commands */ /* response type */ +#define MMC_GO_IDLE_STATE 0 /* R0 */ +#define MMC_SEND_OP_COND 1 /* R3 */ +#define MMC_ALL_SEND_CID 2 /* R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* R1 */ +#define MMC_SWITCH 6 /* R1B */ +#define MMC_SELECT_CARD 7 /* R1 */ +#define MMC_SEND_EXT_CSD 8 /* R1 */ +#define MMC_SEND_CSD 9 /* R2 */ +#define MMC_STOP_TRANSMISSION 12 /* R1B */ +#define MMC_SEND_STATUS 13 /* R1 */ +#define MMC_SET_BLOCKLEN 16 /* R1 */ +#define MMC_READ_BLOCK_SINGLE 17 /* R1 */ +#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */ +#define MMC_SET_BLOCK_COUNT 23 /* R1 */ +#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */ +#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */ +#define MMC_APP_CMD 55 /* R1 */ + +/* SD commands */ /* response type */ +#define SD_SEND_RELATIVE_ADDR 3 /* R6 */ +#define SD_SEND_SWITCH_FUNC 6 /* R1 */ +#define SD_SEND_IF_COND 8 /* R7 */ + +/* SD application commands */ /* response type */ +#define SD_APP_SET_BUS_WIDTH 6 /* R1 */ +#define SD_APP_OP_COND 41 /* R3 */ +#define SD_APP_SEND_SCR 51 /* R1 */ + +/* OCR bits */ +#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */ +#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */ +#define MMC_OCR_SECTOR_MODE (1<<30) +#define MMC_OCR_BYTE_MODE (1<<29) +#define MMC_OCR_3_5V_3_6V (1<<23) +#define MMC_OCR_3_4V_3_5V (1<<22) +#define MMC_OCR_3_3V_3_4V (1<<21) +#define MMC_OCR_3_2V_3_3V (1<<20) +#define MMC_OCR_3_1V_3_2V (1<<19) +#define MMC_OCR_3_0V_3_1V (1<<18) +#define MMC_OCR_2_9V_3_0V (1<<17) +#define MMC_OCR_2_8V_2_9V (1<<16) +#define MMC_OCR_2_7V_2_8V (1<<15) +#define MMC_OCR_2_6V_2_7V (1<<14) +#define MMC_OCR_2_5V_2_6V (1<<13) +#define MMC_OCR_2_4V_2_5V (1<<12) +#define MMC_OCR_2_3V_2_4V (1<<11) +#define MMC_OCR_2_2V_2_3V (1<<10) +#define MMC_OCR_2_1V_2_2V (1<<9) +#define MMC_OCR_2_0V_2_1V (1<<8) +#define MMC_OCR_1_65V_1_95V (1<<7) + +#define SD_OCR_SDHC_CAP (1<<30) +#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */ + +/* R1 response type bits */ +#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */ +#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */ + +/* 48-bit response decoding (32 bits w/o CRC) */ +#define MMC_R1(resp) ((resp)[0]) +#define MMC_R3(resp) ((resp)[0]) +#define SD_R6(resp) ((resp)[0]) +#define MMC_R1_CURRENT_STATE(resp) (((resp)[0] >> 9) & 0xf) + +/* RCA argument and response */ +#define MMC_ARG_RCA(rca) ((rca) << 16) +#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16) + +/* bus width argument */ +#define SD_ARG_BUS_WIDTH_1 0 +#define SD_ARG_BUS_WIDTH_4 2 + +/* EXT_CSD fields */ +#define EXT_CSD_BUS_WIDTH 183 /* WO */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_STRUCTURE 194 /* RO */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_SEC_COUNT 212 /* RO */ + +/* EXT_CSD field definitions */ +#define EXT_CSD_CMD_SET_NORMAL (1U << 0) +#define EXT_CSD_CMD_SET_SECURE (1U << 1) +#define EXT_CSD_CMD_SET_CPSECURE (1U << 2) + +/* EXT_CSD_HS_TIMING */ +#define EXT_CSD_HS_TIMING_BC 0 +#define EXT_CSD_HS_TIMING_HS 1 +#define EXT_CSD_HS_TIMING_HS200 2 +#define EXT_CSD_HS_TIMING_HS400 3 + +/* EXT_CSD_BUS_WIDTH */ +#define EXT_CSD_BUS_WIDTH_1 0 +#define EXT_CSD_BUS_WIDTH_4 1 +#define EXT_CSD_BUS_WIDTH_8 2 +#define EXT_CSD_BUS_WIDTH_4_DDR 5 +#define EXT_CSD_BUS_WIDTH_8_DDR 6 + +/* EXT_CSD_CARD_TYPE */ +/* The only currently valid values for this field are 0x01, 0x03, 0x07, + * 0x0B and 0x0F. */ +#define EXT_CSD_CARD_TYPE_F_26M (1 << 0) +#define EXT_CSD_CARD_TYPE_F_52M (1 << 1) +#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2) +#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3) +#define EXT_CSD_CARD_TYPE_26M 0x01 +#define EXT_CSD_CARD_TYPE_52M 0x03 +#define EXT_CSD_CARD_TYPE_52M_V18 0x07 +#define EXT_CSD_CARD_TYPE_52M_V12 0x0b +#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f + +/* MMC_SWITCH access mode */ +#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ +#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */ +#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits in value */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +/* MMC R2 response (CSD) */ +#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) +#define MMC_CSD_CSDVER_1_0 1 +#define MMC_CSD_CSDVER_2_0 2 +#define MMC_CSD_CSDVER_EXT_CSD 3 +#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4) +#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */ +#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */ +#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */ +#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */ +#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */ +#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) +#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) +#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \ + (MMC_CSD_C_SIZE_MULT((resp))+2)) +#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) + +/* MMC v1 R2 response (CID) */ +#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24) +#define MMC_CID_PNM_V1_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ + (pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \ + (pnm)[7] = '\0'; \ + } while (0) +#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8) +#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24) +#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8) + +/* MMC v2 R2 response (CID) */ +#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8) +#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16) +#define MMC_CID_PNM_V2_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ + (pnm)[6] = '\0'; \ + } while (0) +#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32) + +/* SD R2 response (CSD) */ +#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) +#define SD_CSD_CSDVER_1_0 0 +#define SD_CSD_CSDVER_2_0 1 +#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8) +#define SD_CSD_TAAC_1_5_MSEC 0x26 +#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8) +#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8) +#define SD_CSD_SPEED_25_MHZ 0x32 +#define SD_CSD_SPEED_50_MHZ 0x5a +#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12) +#define SD_CSD_CCC_BASIC (1 << 0) /* basic */ +#define SD_CSD_CCC_BR (1 << 2) /* block read */ +#define SD_CSD_CCC_BW (1 << 4) /* block write */ +#define SD_CSD_CCC_ERASE (1 << 5) /* erase */ +#define SD_CSD_CCC_WP (1 << 6) /* write protection */ +#define SD_CSD_CCC_LC (1 << 7) /* lock card */ +#define SD_CSD_CCC_AS (1 << 8) /*application specific*/ +#define SD_CSD_CCC_IOM (1 << 9) /* I/O mode */ +#define SD_CSD_CCC_SWITCH (1 << 10) /* switch */ +#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) +#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1) +#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1) +#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1) +#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1) +#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) +#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \ + (SD_CSD_C_SIZE_MULT((resp))+2)) +#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22) +#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10) +#define SD_CSD_V2_BL_LEN 0x9 /* 512 */ +#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3) +#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3) +#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3) +#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3) +#define SD_CSD_VDD_RW_CURR_100mA 0x7 +#define SD_CSD_VDD_RW_CURR_80mA 0x6 +#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) +#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1) +#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */ +#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */ +#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1) +#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3) +#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4) +#define SD_CSD_RW_BL_LEN_2G 0xa +#define SD_CSD_RW_BL_LEN_1G 0x9 +#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1) +#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1) +#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1) +#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1) +#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1) +#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2) + +/* SD R2 response (CID) */ +#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8) +#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16) +#define SD_CID_PNM_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = '\0'; \ + } while (0) +#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8) +#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32) +#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12) + +/* SCR (SD Configuration Register) */ +#define SCR_STRUCTURE(scr) MMC_RSP_BITS((scr), 60, 4) +#define SCR_STRUCTURE_VER_1_0 0 /* Version 1.0 */ +#define SCR_SD_SPEC(scr) MMC_RSP_BITS((scr), 56, 4) +#define SCR_SD_SPEC_VER_1_0 0 /* Version 1.0 and 1.01 */ +#define SCR_SD_SPEC_VER_1_10 1 /* Version 1.10 */ +#define SCR_SD_SPEC_VER_2 2 /* Version 2.00 or Version 3.0X */ +#define SCR_DATA_STAT_AFTER_ERASE(scr) MMC_RSP_BITS((scr), 55, 1) +#define SCR_SD_SECURITY(scr) MMC_RSP_BITS((scr), 52, 3) +#define SCR_SD_SECURITY_NONE 0 /* no security */ +#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */ +#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */ +#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4) +#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */ +#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */ +#define SCR_SD_SPEC3(scr) MMC_RSP_BITS((scr), 47, 1) +#define SCR_EX_SECURITY(scr) MMC_RSP_BITS((scr), 43, 4) +#define SCR_SD_SPEC4(scr) MMC_RSP_BITS((scr), 42, 1) +#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 34, 8) +#define SCR_CMD_SUPPORT_CMD23(scr) MMC_RSP_BITS((scr), 33, 1) +#define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1) +#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32) + +/* Status of Switch Function */ +#define SFUNC_STATUS_GROUP(status, group) \ + (__bitfield((uint32_t *)(status), 400 + (group - 1) * 16, 16)) + +#define SD_ACCESS_MODE_SDR12 0 +#define SD_ACCESS_MODE_SDR25 1 +#define SD_ACCESS_MODE_SDR50 2 +#define SD_ACCESS_MODE_SDR104 3 +#define SD_ACCESS_MODE_DDR50 4 + +static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len) +{ + uint32_t mask = (len % 32 == 0) ? UINT_MAX : UINT_MAX >> (32 - (len % 32)); + size_t word = 3 - start / 32; + size_t shift = start % 32; + uint32_t right = src[word] >> shift; + uint32_t left = (len + shift <= 32) ? 0 : src[word - 1] << ((32 - shift) % 32); + return (left | right) & mask; +} + +#endif //_SDMMC_DEFS_H_ diff --git a/components/driver/include/driver/sdmmc_host.h b/components/driver/include/driver/sdmmc_host.h new file mode 100644 index 0000000000..0f56c266a8 --- /dev/null +++ b/components/driver/include/driver/sdmmc_host.h @@ -0,0 +1,165 @@ +// Copyright 2015-2016 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 +#include +#include "esp_err.h" +#include "sdmmc_types.h" +#include "driver/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SDMMC_HOST_SLOT_0 0 ///< SDMMC slot 0 +#define SDMMC_HOST_SLOT_1 1 ///< SDMMC slot 1 + +/** + * @brief Default sdmmc_host_t structure initializer for SDMMC peripheral + * + * Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz + */ +#define SDMMC_HOST_DEFAULT() {\ + .flags = SDMMC_HOST_FLAG_4BIT, \ + .slot = SDMMC_HOST_SLOT_1, \ + .max_freq_khz = SDMMC_FREQ_DEFAULT, \ + .io_voltage = 3.3f, \ + .init = &sdmmc_host_init, \ + .set_bus_width = &sdmmc_host_set_bus_width, \ + .set_card_clk = &sdmmc_host_set_card_clk, \ + .do_transaction = &sdmmc_host_do_transaction, \ + .deinit = &sdmmc_host_deinit, \ +} + +/** + * Extra configuration for SDMMC peripheral slot + */ +typedef struct { + gpio_num_t gpio_cd; ///< GPIO number of card detect signal + gpio_num_t gpio_wp; ///< GPIO number of write protect signal +} sdmmc_slot_config_t; + +#define SDMMC_SLOT_NO_CD ((gpio_num_t) -1) ///< indicates that card detect line is not used +#define SDMMC_SLOT_NO_WP ((gpio_num_t) -1) ///< indicates that write protect line is not used + +/** + * Macro defining default configuration of SDMMC host slot + */ +#define SDMMC_SLOT_CONFIG_DEFAULT() {\ + .gpio_cd = SDMMC_SLOT_NO_CD, \ + .gpio_wp = SDMMC_SLOT_NO_WP, \ +} + +/** + * @brief Initialize SDMMC host peripheral + * + * @note This function is not thread safe + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if sdmmc_host_init was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + */ +esp_err_t sdmmc_host_init(); + +/** + * @brief Initialize given slot of SDMMC peripheral + * + * On the ESP32, SDMMC peripheral has two slots: + * - Slot 0: 8-bit wide, maps to HS1_* signals in PIN MUX + * - Slot 1: 4-bit wide, maps to HS2_* signals in PIN MUX + * + * Card detect and write protect signals can be routed to + * arbitrary GPIOs using GPIO matrix. + * + * @note This function is not thread safe + * + * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1) + * @param slot_config additional configuration for the slot + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if host has not been initialized using sdmmc_host_init + */ +esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config); + +/** + * @brief Select bus width to be used for data transfer + * + * SD/MMC card must be initialized prior to this command, and a command to set + * bus width has to be sent to the card (e.g. SD_APP_SET_BUS_WIDTH) + * + * @note This function is not thread safe + * + * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1) + * @param width bus width (1, 4, or 8 for slot 0; 1 or 4 for slot 1) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if slot number or width is not valid + */ +esp_err_t sdmmc_host_set_bus_width(int slot, size_t width); + +/** + * @brief Set card clock frequency + * + * Currently only integer fractions of 40MHz clock can be used. + * For High Speed cards, 40MHz can be used. + * For Default Speed cards, 20MHz can be used. + * + * @note This function is not thread safe + * + * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1) + * @param freq_khz card clock frequency, in kHz + * @return + * - ESP_OK on success + * - other error codes may be returned in the future + */ +esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz); + +/** + * @brief Send command to the card and get response + * + * This function returns when command is sent and response is received, + * or data is transferred, or timeout occurs. + * + * @note This function is not thread safe w.r.t. init/deinit functions, + * and bus width/clock speed configuration functions. Multiple tasks + * can call sdmmc_host_do_transaction as long as other sdmmc_host_* + * functions are not called. + * + * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1) + * @param cmdinfo pointer to structure describing command and data to transfer + * @return + * - ESP_OK on success + * - ESP_ERR_TIMEOUT if response or data transfer has timed out + * - ESP_ERR_INVALID_CRC if response or data transfer CRC check has failed + * - ESP_ERR_INVALID_RESPONSE if the card has sent an invalid response + */ +esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo); + +/** + * @brief Disable SDMMC host and release allocated resources + * + * @note This function is not thread safe + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if sdmmc_host_init function has not been called + */ +esp_err_t sdmmc_host_deinit(); + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h new file mode 100644 index 0000000000..dfbd2439c6 --- /dev/null +++ b/components/driver/include/driver/sdmmc_types.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2006 Uwe Stuehler + * Adaptations to ESP-IDF Copyright (c) 2016 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. + */ + +#ifndef _SDMMC_TYPES_H_ +#define _SDMMC_TYPES_H_ + +#include +#include +#include "esp_err.h" + +/** + * Decoded values from SD card Card Specific Data register + */ +typedef struct { + int csd_ver; /*!< CSD structure format */ + int mmc_ver; /*!< MMC version (for CID format) */ + int capacity; /*!< total number of sectors */ + int sector_size; /*!< sector size in bytes */ + int read_block_len; /*!< block length for reads */ + int card_command_class; /*!< Card Command Class for SD */ + int tr_speed; /*!< Max transfer speed */ +} sdmmc_csd_t; + +/** + * Decoded values from SD card Card IDentification register + */ +typedef struct { + int mfg_id; /*!< manufacturer identification number */ + int oem_id; /*!< OEM/product identification number */ + char name[8]; /*!< product name (MMC v1 has the longest) */ + int revision; /*!< product revision */ + int serial; /*!< product serial number */ + int date; /*!< manufacturing date */ +} sdmmc_cid_t; + +/** + * Decoded values from SD Configuration Register + */ +typedef struct { + int sd_spec; /*!< SD Physical layer specification version, reported by card */ + int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */ +} sdmmc_scr_t; + +/** + * SD/MMC command response buffer + */ +typedef uint32_t sdmmc_response_t[4]; + +/** + * SD/MMC command information + */ +typedef struct { + uint32_t opcode; /*!< SD or MMC command index */ + uint32_t arg; /*!< SD/MMC command argument */ + sdmmc_response_t response; /*!< response buffer */ + void* data; /*!< buffer to send or read into */ + size_t datalen; /*!< length of data buffer */ + size_t blklen; /*!< block length */ + int flags; /*!< see below */ +#define SCF_ITSDONE 0x0001 /*!< command is complete */ +#define SCF_CMD(flags) ((flags) & 0x00f0) +#define SCF_CMD_AC 0x0000 +#define SCF_CMD_ADTC 0x0010 +#define SCF_CMD_BC 0x0020 +#define SCF_CMD_BCR 0x0030 +#define SCF_CMD_READ 0x0040 /*!< read command (data expected) */ +#define SCF_RSP_BSY 0x0100 +#define SCF_RSP_136 0x0200 +#define SCF_RSP_CRC 0x0400 +#define SCF_RSP_IDX 0x0800 +#define SCF_RSP_PRESENT 0x1000 +/* response types */ +#define SCF_RSP_R0 0 /*!< none */ +#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) +#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136) +#define SCF_RSP_R3 (SCF_RSP_PRESENT) +#define SCF_RSP_R4 (SCF_RSP_PRESENT) +#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) +#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) + esp_err_t error; /*!< error returned from transfer */ +} sdmmc_command_t; + +/** + * SD/MMC Host description + * + * This structure defines properties of SD/MMC host and functions + * of SD/MMC host which can be used by upper layers. + */ +typedef struct { + uint32_t flags; /*!< flags defining host properties */ +#define SDMMC_HOST_FLAG_1BIT BIT(0) /*!< host supports 1-line SD and MMC protocol */ +#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */ +#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */ +#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */ + int slot; /*!< slot number, to be passed to host functions */ + int max_freq_khz; /*!< max frequency supported by the host */ +#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */ +#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */ +#define SDMMC_FREQ_PROBING 4000 /*!< SD/MMC probing speed */ + float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */ + esp_err_t (*init)(void); /*!< Host function to initialize the driver */ + esp_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */ + esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */ + esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */ + esp_err_t (*deinit)(void); /*!< host function to deinitialize the driver */ +} sdmmc_host_t; + +/** + * SD/MMC card information structure + */ +typedef struct { + sdmmc_host_t host; /*!< Host with which the card is associated */ + uint32_t ocr; /*!< OCR (Operation Conditions Register) value */ + sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */ + sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */ + sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */ + uint16_t rca; /*!< RCA (Relative Card Address) */ +} sdmmc_card_t; + + + + +#endif // _SDMMC_TYPES_H_ diff --git a/components/driver/include/driver/spi_master.h b/components/driver/include/driver/spi_master.h new file mode 100644 index 0000000000..83110d9d05 --- /dev/null +++ b/components/driver/include/driver/spi_master.h @@ -0,0 +1,235 @@ +// Copyright 2010-2016 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 _DRIVER_SPI_MASTER_H_ +#define _DRIVER_SPI_MASTER_H_ + +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/** + * @brief Enum with the three SPI peripherals that are software-accessible in it + */ +typedef enum { + SPI_HOST=0, ///< SPI1, SPI + HSPI_HOST=1, ///< SPI2, HSPI + VSPI_HOST=2 ///< SPI3, VSPI +} spi_host_device_t; + + +/** + * @brief This is a configuration structure for a SPI bus. + * + * You can use this structure to specify the GPIO pins of the bus. Normally, the driver will use the + * GPIO matrix to route the signals. An exception is made when all signals either can be routed through + * the IO_MUX or are -1. In that case, the IO_MUX is used, allowing for >40MHz speeds. + */ +typedef struct { + int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used. + int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used. + int sclk_io_num; ///< GPIO pin for Spi CLocK signal, or -1 if not used. + int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used. + int quadhd_io_num; ///< GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used. +} spi_bus_config_t; + + +#define SPI_DEVICE_TXBIT_LSBFIRST (1<<0) ///< Transmit command/address/data LSB first instead of the default MSB first +#define SPI_DEVICE_RXBIT_LSBFIRST (1<<1) ///< Receive data LSB first instead of the default MSB first +#define SPI_DEVICE_BIT_LSBFIRST (SPI_TXBIT_LSBFIRST|SPI_RXBIT_LSBFIRST); ///< Transmit and receive LSB first +#define SPI_DEVICE_3WIRE (1<<2) ///< Use spiq for both sending and receiving data +#define SPI_DEVICE_POSITIVE_CS (1<<3) ///< Make CS positive during a transaction instead of negative +#define SPI_DEVICE_HALFDUPLEX (1<<4) ///< Transmit data before receiving it, instead of simultaneously +#define SPI_DEVICE_CLK_AS_CS (1<<5) ///< Output clock on CS line if CS is active + + +typedef struct spi_transaction_t spi_transaction_t; +typedef void(*transaction_cb_t)(spi_transaction_t *trans); + +/** + * @brief This is a configuration for a SPI slave device that is connected to one of the SPI buses. + */ +typedef struct { + uint8_t command_bits; ///< Amount of bits in command phase (0-16) + uint8_t address_bits; ///< Amount of bits in address phase (0-64) + uint8_t dummy_bits; ///< Amount of dummy bits to insert between address and data phase + uint8_t mode; ///< SPI mode (0-3) + uint8_t duty_cycle_pos; ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128. + uint8_t cs_ena_pretrans; ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions. + uint8_t cs_ena_posttrans; ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16) + int clock_speed_hz; ///< Clock speed, in Hz + int spics_io_num; ///< CS GPIO pin for this device, or -1 if not used + uint32_t flags; ///< Bitwise OR of SPI_DEVICE_* flags + int queue_size; ///< Transaction queue size. This sets how many transactions can be 'in the air' (queued using spi_device_queue_trans but not yet finished using spi_device_get_trans_result) at the same time + transaction_cb_t pre_cb; ///< Callback to be called before a transmission is started. This callback is called within interrupt context. + transaction_cb_t post_cb; ///< Callback to be called after a transmission has completed. This callback is called within interrupt context. +} spi_device_interface_config_t; + + +#define SPI_TRANS_MODE_DIO (1<<0) ///< Transmit/receive data in 2-bit mode +#define SPI_TRANS_MODE_QIO (1<<1) ///< Transmit/receive data in 4-bit mode +#define SPI_TRANS_MODE_DIOQIO_ADDR (1<<2) ///< Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO +#define SPI_TRANS_USE_RXDATA (1<<2) ///< Receive into rx_data member of spi_transaction_t instead into memory at rx_buffer. +#define SPI_TRANS_USE_TXDATA (1<<3) ///< Transmit tx_data member of spi_transaction_t instead of data at tx_buffer. Do not set tx_buffer when using this. + +/** + * This structure describes one SPI transaction + */ +struct spi_transaction_t { + uint32_t flags; ///< Bitwise OR of SPI_TRANS_* flags + uint16_t command; ///< Command data. Specific length was given when device was added to the bus. + uint64_t address; ///< Address. Specific length was given when device was added to the bus. + size_t length; ///< Total data length, in bits + size_t rxlength; ///< Total data length received, if different from length. (0 defaults this to the value of ``length``) + void *user; ///< User-defined variable. Can be used to store eg transaction ID. + union { + const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase + uint8_t tx_data[4]; ///< If SPI_USE_TXDATA is set, data set here is sent directly from this variable. + }; + union { + void *rx_buffer; ///< Pointer to receive buffer, or NULL for no MISO phase + uint8_t rx_data[4]; ///< If SPI_USE_RXDATA is set, data is received directly to this variable + }; +}; + + +typedef struct spi_device_t* spi_device_handle_t; ///< Handle for a device on a SPI bus + +/** + * @brief Initialize a SPI bus + * + * @warning For now, only supports HSPI and VSPI. + * + * @param host SPI peripheral that controls this bus + * @param bus_config Pointer to a spi_bus_config_t struct specifying how the host should be initialized + * @param dma_chan Either 1 or 2. A SPI bus used by this driver must have a DMA channel associated with + * it. The SPI hardware has two DMA channels to share. This parameter indicates which + * one to use. + * @return + * - ESP_ERR_INVALID_ARG if configuration is invalid + * - ESP_ERR_INVALID_STATE if host already is in use + * - ESP_ERR_NO_MEM if out of memory + * - ESP_OK on success + */ +esp_err_t spi_bus_initialize(spi_host_device_t host, spi_bus_config_t *bus_config, int dma_chan); + +/** + * @brief Free a SPI bus + * + * @warning In order for this to succeed, all devices have to be removed first. + * + * @param host SPI peripheral to free + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_INVALID_STATE if not all devices on the bus are freed + * - ESP_OK on success + */ +esp_err_t spi_bus_free(spi_host_device_t host); + +/** + * @brief Allocate a device on a SPI bus + * + * This initializes the internal structures for a device, plus allocates a CS pin on the indicated SPI master + * peripheral and routes it to the indicated GPIO. All SPI master devices have three CS pins and can thus control + * up to three devices. + * + * @param host SPI peripheral to allocate device on + * @param dev_config SPI interface protocol config for the device + * @param handle Pointer to variable to hold the device handle + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_NOT_FOUND if host doesn't have any free CS slots + * - ESP_ERR_NO_MEM if out of memory + * - ESP_OK on success + */ +esp_err_t spi_bus_add_device(spi_host_device_t host, spi_device_interface_config_t *dev_config, spi_device_handle_t *handle); + + +/** + * @brief Remove a device from the SPI bus + * + * @param handle Device handle to free + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_INVALID_STATE if device already is freed + * - ESP_OK on success + */ +esp_err_t spi_bus_remove_device(spi_device_handle_t handle); + + +/** + * @brief Queue a SPI transaction for execution + * + * @param handle Device handle obtained using spi_host_add_dev + * @param trans_desc Description of transaction to execute + * @param ticks_to_wait Ticks to wait until there's room in the queue; use portMAX_DELAY to + * never time out. + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_OK on success + */ +esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait); + + +/** + * @brief Get the result of a SPI transaction queued earlier + * + * This routine will wait until a transaction to the given device (queued earlier with + * spi_device_queue_trans) has succesfully completed. It will then return the description of the + * completed transaction so software can inspect the result and e.g. free the memory or + * re-use the buffers. + * + * @param handle Device handle obtained using spi_host_add_dev + * @param trans_desc Pointer to variable able to contain a pointer to the description of the + * transaction that is executed + * @param ticks_to_wait Ticks to wait until there's a returned item; use portMAX_DELAY to never time + out. + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_OK on success + */ +esp_err_t spi_device_get_trans_result(spi_device_handle_t handle, spi_transaction_t **trans_desc, TickType_t ticks_to_wait); + + +/** + * @brief Do a SPI transaction + * + * Essentially does the same as spi_device_queue_trans followed by spi_device_get_trans_result. Do + * not use this when there is still a transaction queued that hasn't been finalized + * using spi_device_get_trans_result. + * + * @param handle Device handle obtained using spi_host_add_dev + * @param trans_desc Pointer to variable able to contain a pointer to the description of the + * transaction that is executed + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_OK on success + */ +esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *trans_desc); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index c193fb0ef8..68d02a5e0a 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -477,7 +477,8 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * @param tx_buffer_size UART TX ring buffer size. * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * @param queue_size UART event queue size/depth. - * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @param uart_queue UART event queue handle (out param). On success, a new queue handle is written here to provide + * access to UART events. If set to NULL, driver will not use an event queue. * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * @@ -485,7 +486,7 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t* uart_queue, int intr_alloc_flags); /** * @brief Uninstall UART driver. @@ -647,7 +648,7 @@ esp_err_t uart_enable_pattern_det_intr(uart_port_t uart_num, char pattern_chr, u * //a. Set UART parameter * int uart_num = 0; //uart port number * uart_config_t uart_config = { - * .baud_rate = UART_BITRATE_115200, //baudrate + * .baud_rate = 115200, //baudrate * .data_bits = UART_DATA_8_BITS, //data bit mode * .parity = UART_PARITY_DISABLE, //parity mode * .stop_bits = UART_STOP_BITS_1, //stop bit mode diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 77ca975969..6944f1b115 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -19,6 +19,8 @@ #include "freertos/xtensa_api.h" #include "soc/gpio_sig_map.h" #include "driver/ledc.h" +#include "soc/ledc_reg.h" +#include "soc/ledc_struct.h" #include "esp_log.h" static const char* LEDC_TAG = "ledc"; @@ -29,26 +31,56 @@ static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; return (ret_val); \ } +typedef struct { + uint16_t speed_mode; + uint16_t direction; + int target_duty; + int cycle_num; + int scale; + ledc_fade_mode_t mode; + xSemaphoreHandle ledc_fade_sem; + xSemaphoreHandle ledc_fade_mux; +} ledc_fade_t; +static ledc_fade_t* s_ledc_fade_rec = NULL; +static ledc_isr_handle_t s_ledc_fade_isr_handle = NULL; + +#define LEDC_VAL_NO_CHANGE (-1) +#define LEDC_STEP_NUM_MAX (1023) +#define LEDC_DUTY_DECIMAL_BIT_NUM (4) + +#define LEDC_MODE_ERR_STR "LEDC mode error" +#define LEDC_TIMER_ERR_STR "LEDC timer error" +#define LEDC_CHANNEL_ERR_STR "LEDC channel error" +#define LEDC_GPIO_OUT_ERR_STR "LEDC GPIO output number error" +#define LEDC_FADE_DIR_ERR_STR "LEDC fade direction error" +#define LEDC_FADE_SERVICE_ERR_STR "LEDC fade service not installed" +#define LEDC_FADE_TARGET_ERR_STR "LEDC fade target duty error" +#define LEDC_FADE_INSTALLED_ERR_STR "LEDC fade service already installed" +#define LEDC_FADE_MODE_ERR_STR "LEDC fade mode error" + + esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num; LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src; LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num = bit_num; - if(speed_mode != LEDC_HIGH_SPEED_MODE) { + if (speed_mode != LEDC_HIGH_SPEED_MODE) { LEDC.timer_group[speed_mode].timer[timer_sel].conf.low_speed_update = 1; } portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; } -static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, uint32_t hpoint_val, uint32_t duty_val, +static IRAM_ATTR esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel_num, int hpoint_val, uint32_t duty_val, uint32_t duty_direction, uint32_t duty_num, uint32_t duty_cycle, uint32_t duty_scale) { portENTER_CRITICAL(&ledc_spinlock); - LEDC.channel_group[speed_mode].channel[channel_num].hpoint.hpoint = hpoint_val; + if (hpoint_val >= 0) { + LEDC.channel_group[speed_mode].channel[channel_num].hpoint.hpoint = hpoint_val; + } LEDC.channel_group[speed_mode].channel[channel_num].duty.duty = duty_val; LEDC.channel_group[speed_mode].channel[channel_num].conf1.val = ((duty_direction & LEDC_DUTY_INC_HSCH0_V) << LEDC_DUTY_INC_HSCH0_S) | ((duty_num & LEDC_DUTY_NUM_HSCH0_V) << LEDC_DUTY_NUM_HSCH0_S) | @@ -58,10 +90,10 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, return ESP_OK; } -esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx) +esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t timer_idx) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_idx <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx; portEXIT_CRITICAL(&ledc_spinlock); @@ -70,8 +102,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1; LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0; @@ -81,8 +113,8 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1; portEXIT_CRITICAL(&ledc_spinlock); @@ -91,8 +123,8 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0; portEXIT_CRITICAL(&ledc_spinlock); @@ -101,15 +133,15 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); uint32_t value; uint32_t intr_type = type; portENTER_CRITICAL(&ledc_spinlock); value = LEDC.int_ena.val; - if(intr_type == LEDC_INTR_FADE_END) { + if (intr_type == LEDC_INTR_FADE_END) { LEDC.int_ena.val = value | BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel); } else { - LEDC.int_ena.val = (value & (~(BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel)))); + LEDC.int_ena.val = ( value & ( ~( BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel) ) ) ); } portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; @@ -120,7 +152,7 @@ esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, esp_err_t ret; LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); - ret=esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + ret = esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); portEXIT_CRITICAL(&ledc_spinlock); return ret; } @@ -131,32 +163,35 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) int bit_num = timer_conf->bit_num; int timer_num = timer_conf->timer_num; int speed_mode = timer_conf->speed_mode; - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - if(freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + periph_module_enable(PERIPH_LEDC_MODULE); + if (freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u", freq_hz, bit_num); return ESP_ERR_INVALID_ARG; } - if(timer_num > LEDC_TIMER_3) { + if (timer_num > LEDC_TIMER_3) { ESP_LOGE(LEDC_TAG, "invalid timer #%u", timer_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; - uint32_t precision = (0x1 << bit_num); // 2**depth + uint32_t precision = ( 0x1 << bit_num ); // 2**depth // Try calculating divisor based on LEDC_APB_CLK ledc_clk_src_t timer_clk_src = LEDC_APB_CLK; // div_param is a Q10.8 fixed point value - uint64_t div_param = ((uint64_t) LEDC_APB_CLK_HZ << 8) / freq_hz / precision; + uint64_t div_param = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision; if (div_param < 256) { // divisor is too low - ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try reducing freq_hz or bit_num. div_param=%d", (uint32_t) div_param); + ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try reducing freq_hz or bit_num. div_param=%d", + (uint32_t ) div_param); ret = ESP_FAIL; } if (div_param > LEDC_DIV_NUM_HSTIMER0_V) { // APB_CLK results in divisor which too high. Try using REF_TICK as clock source. timer_clk_src = LEDC_REF_TICK; - div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; - if(div_param < 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { - ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try increasing freq_hz or bit_num. div_param=%d", (uint32_t) div_param); + div_param = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / freq_hz / precision; + if (div_param < 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { + ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try increasing freq_hz or bit_num. div_param=%d", + (uint32_t ) div_param); ret = ESP_FAIL; } } @@ -169,12 +204,12 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel) { - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), LEDC_GPIO_OUT_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO); gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT); - if(speed_mode == LEDC_HIGH_SPEED_MODE) { + if (speed_mode == LEDC_HIGH_SPEED_MODE) { gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0); } else { ESP_LOGE(LEDC_TAG, "low speed mode is not implemented"); @@ -191,10 +226,10 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) uint32_t timer_select = ledc_conf->timer_sel; uint32_t intr_type = ledc_conf->intr_type; uint32_t duty = ledc_conf->duty; - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), LEDC_GPIO_OUT_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_select <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); periph_module_enable(PERIPH_LEDC_MODULE); esp_err_t ret = ESP_OK; /*set channel parameters*/ @@ -207,7 +242,7 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) ledc_bind_channel_timer(speed_mode, ledc_channel, timer_select); /*set interrupt type*/ ledc_enable_intr_type(speed_mode, ledc_channel, intr_type); - ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u", + ESP_LOGD(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u", ledc_channel, gpio_num, duty, timer_select ); /*set LEDC signal in gpio matrix*/ @@ -219,8 +254,8 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; @@ -230,8 +265,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1; LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0; @@ -239,16 +274,20 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; } -esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, + +esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error", ESP_ERR_INVALID_ARG); - if(step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, LEDC_FADE_DIR_ERR_STR, ESP_ERR_INVALID_ARG); + if (step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u", step_num, duty_cyle_num, duty_scale); return ESP_ERR_INVALID_ARG; } + if (s_ledc_fade_rec) { + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); + } ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -263,8 +302,11 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + if (s_ledc_fade_rec) { + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); + } ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -279,26 +321,26 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (-1)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, (-1)); uint32_t duty = (LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4); return duty; } esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); esp_err_t ret = ESP_OK; uint32_t div_num = 0; uint32_t bit_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.bit_num; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; - uint32_t precision = (0x1 << bit_num); - if(timer_source_clk == LEDC_APB_CLK) { - div_num = ((uint64_t) LEDC_APB_CLK_HZ << 8) / freq_hz / precision; + uint32_t precision = ( 0x1 << bit_num ); + if (timer_source_clk == LEDC_APB_CLK) { + div_num = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision; } else { - div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; + div_num = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / freq_hz / precision; } - if(div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { + if (div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", div_num); ret = ESP_FAIL; } @@ -309,18 +351,217 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (0)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ( 0 )); portENTER_CRITICAL(&ledc_spinlock); uint32_t freq = 0; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; uint32_t bit_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.bit_num; uint32_t div_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num; - uint32_t precision = (0x1 << bit_num); - if(timer_source_clk == LEDC_APB_CLK) { - freq = ((uint64_t) LEDC_APB_CLK_HZ << 8) / precision / div_num; + uint32_t precision = ( 0x1 << bit_num ); + if (timer_source_clk == LEDC_APB_CLK) { + freq = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / precision / div_num; } else { - freq = ((uint64_t) LEDC_REF_CLK_HZ << 8) / precision / div_num; + freq = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / precision / div_num; } portEXIT_CRITICAL(&ledc_spinlock); return freq; } + +void IRAM_ATTR ledc_fade_isr() +{ + int i; + portBASE_TYPE HPTaskAwoken = pdFALSE; + uint32_t intr_status = LEDC.int_st.val; //read LEDC interrupt status. + LEDC.int_clr.val = intr_status; //clear LEDC interrupt status. + for (i = 0; i < 8; i++) { + if (intr_status & BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ST_S + i)) { + int speed_mode = s_ledc_fade_rec[i].speed_mode; + int duty_cur = LEDC.channel_group[speed_mode].channel[i].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM; + if (duty_cur == s_ledc_fade_rec[i].target_duty) { + if(s_ledc_fade_rec[i].mode == LEDC_FADE_WAIT_DONE) { + xSemaphoreGiveFromISR(s_ledc_fade_rec[i].ledc_fade_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } + } + continue; + } + int duty_tar = s_ledc_fade_rec[i].target_duty; + int scale = s_ledc_fade_rec[i].scale; + if (scale == 0) { + continue; + } + int cycle = s_ledc_fade_rec[i].cycle_num; + int delta = s_ledc_fade_rec[i].direction == LEDC_DUTY_DIR_DECREASE ? duty_cur - duty_tar : duty_tar - duty_cur; + int step = delta / scale > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : delta / scale; + + if (delta > scale) { + ledc_duty_config( + speed_mode, + i, + LEDC_VAL_NO_CHANGE, + duty_cur << LEDC_DUTY_DECIMAL_BIT_NUM, + s_ledc_fade_rec[i].direction, + step, + cycle, + scale); + } else { + ledc_duty_config( + speed_mode, + i, + LEDC_VAL_NO_CHANGE, + duty_tar << LEDC_DUTY_DECIMAL_BIT_NUM, + s_ledc_fade_rec[i].direction, + 1, + 1, + 0); + } + LEDC.channel_group[speed_mode].channel[i].conf1.duty_start = 1; + } + } + LEDC.int_clr.val = intr_status; //clear LEDC interrupt status. +} + +esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int max_fade_time_ms) +{ + int timer_sel = LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel; + int max_duty = ( 1 << ( LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num ) ) - 1; + LEDC_CHECK(target_duty <= max_duty, LEDC_FADE_TARGET_ERR_STR, ESP_ERR_INVALID_ARG); + uint32_t freq = ledc_get_freq(speed_mode, timer_sel); + int duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM; + int duty_delta = target_duty > duty_cur ? target_duty - duty_cur : duty_cur - target_duty; + + if (duty_delta == 0) { + return ESP_OK; + } + int total_cycles = max_fade_time_ms * freq / 1000; + int scale, cycle_num; + if (total_cycles > duty_delta) { + scale = 1; + cycle_num = total_cycles / duty_delta; + } else { + cycle_num = 1; + scale = ( duty_delta + total_cycles - 1 ) / total_cycles; + } + return ledc_set_fade_with_step(speed_mode, channel, target_duty, scale, cycle_num); +} + +esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int scale, int cycle_num) +{ + LEDC_CHECK(s_ledc_fade_rec != NULL, LEDC_FADE_SERVICE_ERR_STR, ESP_ERR_INVALID_STATE); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel < LEDC_CHANNEL_MAX, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + int timer_sel = LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel; + int max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num)) - 1; + LEDC_CHECK(target_duty <= max_duty, LEDC_FADE_TARGET_ERR_STR, ESP_ERR_INVALID_ARG); + //disable the interrupt, so the operation will not mess up + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); + + portENTER_CRITICAL(&ledc_spinlock); + int duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM; + int duty_delta = target_duty > duty_cur ? target_duty - duty_cur : duty_cur - target_duty; + if (duty_delta == 0) { + return ESP_OK; + } + s_ledc_fade_rec[channel].speed_mode = speed_mode; + s_ledc_fade_rec[channel].target_duty = target_duty; + s_ledc_fade_rec[channel].cycle_num = cycle_num; + s_ledc_fade_rec[channel].scale = scale; + int step_num; + if (duty_cur > target_duty) { + s_ledc_fade_rec[channel].direction = LEDC_DUTY_DIR_DECREASE; + step_num = ( duty_cur - target_duty ) / scale; + step_num = step_num > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : step_num; + } else { + s_ledc_fade_rec[channel].direction = LEDC_DUTY_DIR_INCREASE; + step_num = ( target_duty - duty_cur ) / scale; + step_num = step_num > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : step_num; + } + portEXIT_CRITICAL(&ledc_spinlock); + + ledc_set_fade( + speed_mode, + channel, + duty_cur, + s_ledc_fade_rec[channel].direction, + step_num, + s_ledc_fade_rec[channel].cycle_num, + s_ledc_fade_rec[channel].scale + ); + ESP_LOGD(LEDC_TAG, "cur duty: %d; target: %d, step: %d, cycle: %d; scale: %d\n", + LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM, + target_duty, + step_num, + s_ledc_fade_rec[channel].cycle_num, + s_ledc_fade_rec[channel].scale + ); + LEDC.int_clr.val |= BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel); + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_FADE_END); + return ESP_OK; +} + +esp_err_t ledc_fade_func_install(int intr_alloc_flags) +{ + LEDC_CHECK(s_ledc_fade_rec == NULL, LEDC_FADE_INSTALLED_ERR_STR, ESP_ERR_INVALID_STATE); + s_ledc_fade_rec = (ledc_fade_t*) calloc(LEDC_CHANNEL_MAX, sizeof(ledc_fade_t)); + if (s_ledc_fade_rec == NULL) { + return ESP_ERR_NO_MEM; + } + int i = 0; + for (i = 0; i < LEDC_CHANNEL_MAX; i++) { + s_ledc_fade_rec[i].ledc_fade_sem = xSemaphoreCreateBinary(); + s_ledc_fade_rec[i].ledc_fade_mux = xSemaphoreCreateMutex(); + if (s_ledc_fade_rec[i].ledc_fade_sem == NULL || s_ledc_fade_rec[i].ledc_fade_mux == NULL) { + ledc_fade_func_uninstall(); + return ESP_ERR_NO_MEM; + } + } + //OR intr_alloc_flags with ESP_INTR_FLAG_IRAM because the fade isr is in IRAM + ledc_isr_register(ledc_fade_isr, NULL, intr_alloc_flags | ESP_INTR_FLAG_IRAM, &s_ledc_fade_isr_handle); + return ESP_OK; +} + +void ledc_fade_func_uninstall() +{ + if (s_ledc_fade_rec == NULL) { + return; + } + if(s_ledc_fade_isr_handle) { + esp_intr_free(s_ledc_fade_isr_handle); + s_ledc_fade_isr_handle = NULL; + } + int i; + for (i = 0; i < LEDC_CHANNEL_MAX; i++) { + if (s_ledc_fade_rec[i].ledc_fade_sem) { + xSemaphoreHandle sem_tmp = s_ledc_fade_rec[i].ledc_fade_sem; + s_ledc_fade_rec[i].ledc_fade_sem = NULL; + vSemaphoreDelete(sem_tmp); + } + if (s_ledc_fade_rec[i].ledc_fade_mux) { + xSemaphoreHandle mux_tmp = s_ledc_fade_rec[i].ledc_fade_mux; + s_ledc_fade_rec[i].ledc_fade_mux = NULL; + vSemaphoreDelete(mux_tmp); + } + } + free(s_ledc_fade_rec); + s_ledc_fade_rec = NULL; + return; +} + +esp_err_t ledc_fade_start(ledc_channel_t channel, ledc_fade_mode_t wait_done) +{ + LEDC_CHECK(s_ledc_fade_rec != NULL, LEDC_FADE_SERVICE_ERR_STR, ESP_ERR_INVALID_STATE); + LEDC_CHECK(wait_done < LEDC_FADE_MAX, LEDC_FADE_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + int speed_mode = s_ledc_fade_rec[channel].speed_mode; + xSemaphoreTake(s_ledc_fade_rec[channel].ledc_fade_mux, portMAX_DELAY); + if (wait_done == LEDC_FADE_WAIT_DONE) { + s_ledc_fade_rec[channel].mode = LEDC_FADE_WAIT_DONE; + ledc_update_duty(speed_mode, channel); + xSemaphoreTake(s_ledc_fade_rec[channel].ledc_fade_sem, portMAX_DELAY); + } else { + s_ledc_fade_rec[channel].mode = LEDC_FADE_NO_WAIT; + ledc_update_duty(speed_mode, channel); + } + xSemaphoreGive(s_ledc_fade_rec[channel].ledc_fade_mux); + return ESP_OK; +} diff --git a/components/driver/periph_ctrl.c b/components/driver/periph_ctrl.c index 7fc4091aa5..d90fa595f9 100644 --- a/components/driver/periph_ctrl.c +++ b/components/driver/periph_ctrl.c @@ -97,6 +97,18 @@ void periph_module_enable(periph_module_t periph) SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_PCNT_CLK_EN); CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_PCNT_RST); break; + case PERIPH_SPI_MODULE: + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_1); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_1); + break; + case PERIPH_HSPI_MODULE: + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST); + break; + case PERIPH_VSPI_MODULE: + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_2); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_2); + break; default: break; } @@ -179,6 +191,18 @@ void periph_module_disable(periph_module_t periph) CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_PCNT_CLK_EN); SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_PCNT_RST); break; + case PERIPH_SPI_MODULE: + CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_1); + SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_1); + break; + case PERIPH_HSPI_MODULE: + CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN); + SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST); + break; + case PERIPH_VSPI_MODULE: + CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_2); + SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_2); + break; default: break; } diff --git a/components/driver/rmt.c b/components/driver/rmt.c index e29f190024..d277ea00cd 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -341,7 +341,7 @@ esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en) return ESP_OK; } -esp_err_t rmt_set_evt_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh) +esp_err_t rmt_set_tx_thr_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh) { RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(evt_thresh < 256, "RMT EVT THRESH ERR", ESP_ERR_INVALID_ARG); @@ -380,10 +380,16 @@ esp_err_t rmt_config(rmt_config_t* rmt_param) uint8_t gpio_num = rmt_param->gpio_num; uint8_t mem_cnt = rmt_param->mem_block_num; int clk_div = rmt_param->clk_div; + uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz; + bool carrier_en = rmt_param->tx_config.carrier_en; RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(GPIO_IS_VALID_GPIO(gpio_num), RMT_GPIO_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK((mem_cnt + channel <= 8 && mem_cnt > 0), RMT_MEM_CNT_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK((clk_div > 0), RMT_CLK_DIV_ERROR_STR, ESP_ERR_INVALID_ARG); + if (mode == RMT_MODE_TX) { + RMT_CHECK((!carrier_en || carrier_freq_hz > 0), "RMT carrier frequency can't be zero", ESP_ERR_INVALID_ARG); + } + periph_module_enable(PERIPH_RMT_MODULE); RMT.conf_ch[channel].conf0.div_cnt = clk_div; @@ -397,7 +403,6 @@ esp_err_t rmt_config(rmt_config_t* rmt_param) if(mode == RMT_MODE_TX) { uint32_t rmt_source_clk_hz = 0; - uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz; uint16_t carrier_duty_percent = rmt_param->tx_config.carrier_duty_percent; uint8_t carrier_level = rmt_param->tx_config.carrier_level; uint8_t idle_level = rmt_param->tx_config.idle_level; @@ -416,16 +421,23 @@ esp_err_t rmt_config(rmt_config_t* rmt_param) portEXIT_CRITICAL(&rmt_spinlock); /*Set carrier*/ - uint32_t duty_div, duty_h, duty_l; - duty_div = rmt_source_clk_hz / carrier_freq_hz; - duty_h = duty_div * carrier_duty_percent / 100; - duty_l = duty_div - duty_h; - RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; - RMT.carrier_duty_ch[channel].high = duty_h; - RMT.carrier_duty_ch[channel].low = duty_l; - RMT.conf_ch[channel].conf0.carrier_en = rmt_param->tx_config.carrier_en; + RMT.conf_ch[channel].conf0.carrier_en = carrier_en; + if (carrier_en) { + uint32_t duty_div, duty_h, duty_l; + duty_div = rmt_source_clk_hz / carrier_freq_hz; + duty_h = duty_div * carrier_duty_percent / 100; + duty_l = duty_div - duty_h; + RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; + RMT.carrier_duty_ch[channel].high = duty_h; + RMT.carrier_duty_ch[channel].low = duty_l; + } else { + RMT.conf_ch[channel].conf0.carrier_out_lv = 0; + RMT.carrier_duty_ch[channel].high = 0; + RMT.carrier_duty_ch[channel].low = 0; + } ESP_LOGD(RMT_TAG, "Rmt Tx Channel %u|Gpio %u|Sclk_Hz %u|Div %u|Carrier_Hz %u|Duty %u", - channel, gpio_num, rmt_source_clk_hz, clk_div, carrier_freq_hz, carrier_duty_percent); + channel, gpio_num, rmt_source_clk_hz, clk_div, carrier_freq_hz, carrier_duty_percent); + } else if(RMT_MODE_RX == mode) { uint8_t filter_cnt = rmt_param->rx_config.filter_ticks_thresh; @@ -612,7 +624,7 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel) rmt_set_rx_intr_en(channel, 0); rmt_set_err_intr_en(channel, 0); rmt_set_tx_intr_en(channel, 0); - rmt_set_evt_intr_en(channel, 0, 0xffff); + rmt_set_tx_thr_intr_en(channel, 0, 0xffff); if(p_rmt_obj[channel]->tx_sem) { vSemaphoreDelete(p_rmt_obj[channel]->tx_sem); p_rmt_obj[channel]->tx_sem = NULL; @@ -685,7 +697,7 @@ esp_err_t rmt_write_items(rmt_channel_t channel, rmt_item32_t* rmt_item, int ite RMT.apb_conf.mem_tx_wrap_en = 1; len_rem -= item_block_len; RMT.conf_ch[channel].conf1.tx_conti_mode = 0; - rmt_set_evt_intr_en(channel, 1, item_sub_len); + rmt_set_tx_thr_intr_en(channel, 1, item_sub_len); p_rmt->tx_data = rmt_item + item_block_len; p_rmt->tx_len_rem = len_rem; p_rmt->tx_offset = 0; diff --git a/components/driver/sdmmc_host.c b/components/driver/sdmmc_host.c new file mode 100644 index 0000000000..400c85b889 --- /dev/null +++ b/components/driver/sdmmc_host.c @@ -0,0 +1,463 @@ +// Copyright 2015-2016 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. + +#include +#include +#include +#include "esp_log.h" +#include "esp_intr_alloc.h" +#include "soc/sdmmc_struct.h" +#include "soc/sdmmc_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" +#include "rom/gpio.h" +#include "driver/gpio.h" +#include "driver/sdmmc_host.h" +#include "sdmmc_private.h" + +#define SDMMC_EVENT_QUEUE_LENGTH 32 + +typedef struct { + uint32_t clk; + uint32_t cmd; + uint32_t d0; + uint32_t d1; + uint32_t d2; + uint32_t d3; + uint32_t d4; + uint32_t d5; + uint32_t d6; + uint32_t d7; + uint8_t card_detect; + uint8_t write_protect; + uint8_t width; +} sdmmc_slot_info_t; + + +static void sdmmc_isr(void* arg); +static void sdmmc_host_dma_init(); + +static const sdmmc_slot_info_t s_slot_info[2] = { + { + .clk = PERIPHS_IO_MUX_SD_CLK_U, + .cmd = PERIPHS_IO_MUX_SD_CMD_U, + .d0 = PERIPHS_IO_MUX_SD_DATA0_U, + .d1 = PERIPHS_IO_MUX_SD_DATA1_U, + .d2 = PERIPHS_IO_MUX_SD_DATA2_U, + .d3 = PERIPHS_IO_MUX_SD_DATA3_U, + .d4 = PERIPHS_IO_MUX_GPIO16_U, + .d5 = PERIPHS_IO_MUX_GPIO17_U, + .d6 = PERIPHS_IO_MUX_GPIO5_U, + .d7 = PERIPHS_IO_MUX_GPIO18_U, + .card_detect = HOST_CARD_DETECT_N_1_IDX, + .write_protect = HOST_CARD_WRITE_PRT_1_IDX, + .width = 8 + }, + { + .clk = PERIPHS_IO_MUX_MTMS_U, + .cmd = PERIPHS_IO_MUX_MTDO_U, + .d0 = PERIPHS_IO_MUX_GPIO2_U, + .d1 = PERIPHS_IO_MUX_GPIO4_U, + .d2 = PERIPHS_IO_MUX_MTDI_U, + .d3 = PERIPHS_IO_MUX_MTCK_U, + .card_detect = HOST_CARD_DETECT_N_2_IDX, + .write_protect = HOST_CARD_WRITE_PRT_2_IDX, + .width = 4 + } +}; + +static const char* TAG = "sdmmc_periph"; +static intr_handle_t s_intr_handle; +static QueueHandle_t s_event_queue; + + +void sdmmc_host_reset() +{ + // Set reset bits + SDMMC.ctrl.controller_reset = 1; + SDMMC.ctrl.dma_reset = 1; + SDMMC.ctrl.fifo_reset = 1; + // Wait for the reset bits to be cleared by hardware + while (SDMMC.ctrl.controller_reset || SDMMC.ctrl.fifo_reset || SDMMC.ctrl.dma_reset) { + ; + } +} + +/* We have two clock divider stages: + * - one is the clock generator which drives SDMMC peripheral, + * it can be configured using SDMMC.clock register. It can generate + * frequencies 160MHz/(N + 1), where 0 < N < 16, I.e. from 10 to 80 MHz. + * - 4 clock dividers inside SDMMC peripheral, which can divide clock + * from the first stage by 2 * M, where 0 < M < 255 + * (they can also be bypassed). + * + * For cards which aren't UHS-1 or UHS-2 cards, which we don't support, + * maximum bus frequency in high speed (HS) mode is 50 MHz. + * Note: for non-UHS-1 cards, HS mode is optional. + * Default speed (DS) mode is mandatory, it works up to 25 MHz. + * Whether the card supports HS or not can be determined using TRAN_SPEED + * field of card's CSD register. + * + * 50 MHz can not be obtained exactly, closest we can get is 53 MHz. + * For now set the first stage divider to generate 40MHz, and then configure + * the second stage dividers to generate the frequency requested. + * + * Of the second stage dividers, div0 is used for card 0, and div1 is used + * for card 1. + */ + +static void sdmmc_host_input_clk_enable() +{ + // Set frequency to 160MHz / (p + 1) = 40MHz, duty cycle (h + 1)/(p + 1) = 1/2 + SDMMC.clock.div_factor_p = 3; + SDMMC.clock.div_factor_h = 1; + SDMMC.clock.div_factor_m = 3; + // Set phases for in/out clocks + SDMMC.clock.phase_dout = 4; + SDMMC.clock.phase_din = 4; + SDMMC.clock.phase_core = 0; + // Wait for the clock to propagate + ets_delay_us(10); +} + +static void sdmmc_host_input_clk_disable() +{ + SDMMC.clock.val = 0; +} + +static void sdmmc_host_clock_update_command(int slot) +{ + // Clock update command (not a real command; just updates CIU registers) + sdmmc_hw_cmd_t cmd_val = { + .card_num = slot, + .update_clk_reg = 1, + .wait_complete = 1 + }; + bool repeat = true; + while(repeat) { + sdmmc_host_start_command(slot, cmd_val, 0); + while (true) { + // Sending clock update command to the CIU can generate HLE error. + // According to the manual, this is okay and we must retry the command. + if (SDMMC.rintsts.hle) { + SDMMC.rintsts.hle = 1; + repeat = true; + break; + } + // When the command is accepted by CIU, start_command bit will be + // cleared in SDMMC.cmd register. + if (SDMMC.cmd.start_command == 0) { + repeat = false; + break; + } + } + } +} + +esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz) +{ + if (!(slot == 0 || slot == 1)) { + return ESP_ERR_INVALID_ARG; + } + const int clk40m = 40000; + + // Disable clock first + SDMMC.clkena.cclk_enable &= ~BIT(slot); + sdmmc_host_clock_update_command(slot); + + // Calculate new dividers + int div = 0; + if (freq_khz < clk40m) { + // round up; extra *2 is because clock divider divides by 2*n + div = (clk40m + freq_khz * 2 - 1) / (freq_khz * 2); + } + ESP_LOGD(TAG, "slot=%d div=%d freq=%dkHz", slot, div, + (div == 0) ? clk40m : clk40m / (2 * div)); + + // Program CLKDIV and CLKSRC, send them to the CIU + switch(slot) { + case 0: + SDMMC.clksrc.card0 = 0; + SDMMC.clkdiv.div0 = div; + break; + case 1: + SDMMC.clksrc.card1 = 1; + SDMMC.clkdiv.div1 = div; + break; + } + sdmmc_host_clock_update_command(slot); + + // Re-enable clocks + SDMMC.clkena.cclk_enable |= BIT(slot); + SDMMC.clkena.cclk_low_power |= BIT(slot); + sdmmc_host_clock_update_command(slot); + return ESP_OK; +} + +esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg) { + if (!(slot == 0 || slot == 1)) { + return ESP_ERR_INVALID_ARG; + } + while (SDMMC.cmd.start_command == 1) { + ; + } + SDMMC.cmdarg = arg; + cmd.card_num = slot; + cmd.start_command = 1; + SDMMC.cmd = cmd; + return ESP_OK; +} + +esp_err_t sdmmc_host_init() +{ + if (s_intr_handle) { + return ESP_ERR_INVALID_STATE; + } + + // Enable clock to peripheral + sdmmc_host_input_clk_enable(); + + // Reset + sdmmc_host_reset(); + ESP_LOGD(TAG, "peripheral version %x, hardware config %08x", SDMMC.verid, SDMMC.hcon); + + // Clear interrupt status and set interrupt mask to known state + SDMMC.rintsts.val = 0xffffffff; + SDMMC.intmask.val = 0; + SDMMC.ctrl.int_enable = 0; + + // Allocate event queue + s_event_queue = xQueueCreate(SDMMC_EVENT_QUEUE_LENGTH, sizeof(sdmmc_event_t)); + if (!s_event_queue) { + return ESP_ERR_NO_MEM; + } + // Attach interrupt handler + esp_err_t ret = esp_intr_alloc(ETS_SDIO_HOST_INTR_SOURCE, 0, &sdmmc_isr, s_event_queue, &s_intr_handle); + if (ret != ESP_OK) { + vQueueDelete(s_event_queue); + s_event_queue = NULL; + return ret; + } + // Enable interrupts + SDMMC.intmask.val = + SDMMC_INTMASK_CD | + SDMMC_INTMASK_CMD_DONE | + SDMMC_INTMASK_DATA_OVER | + SDMMC_INTMASK_RCRC | SDMMC_INTMASK_DCRC | + SDMMC_INTMASK_RTO | SDMMC_INTMASK_DTO | SDMMC_INTMASK_HTO | + SDMMC_INTMASK_SBE | SDMMC_INTMASK_EBE | + SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE; + SDMMC.ctrl.int_enable = 1; + + // Enable DMA + sdmmc_host_dma_init(); + + // Initialize transaction handler + ret = sdmmc_host_transaction_handler_init(); + if (ret != ESP_OK) { + vQueueDelete(s_event_queue); + s_event_queue = NULL; + esp_intr_free(s_intr_handle); + s_intr_handle = NULL; + return ret; + } + + return ESP_OK; +} + + +static inline void configure_pin(uint32_t io_mux_reg) +{ + const int sdmmc_func = 3; + const int drive_strength = 3; + PIN_INPUT_ENABLE(io_mux_reg); + PIN_FUNC_SELECT(io_mux_reg, sdmmc_func); + PIN_SET_DRV(io_mux_reg, drive_strength); +} + +esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config) +{ + if (!s_intr_handle) { + return ESP_ERR_INVALID_STATE; + } + if (!(slot == 0 || slot == 1)) { + return ESP_ERR_INVALID_ARG; + } + if (slot_config == NULL) { + return ESP_ERR_INVALID_ARG; + } + int gpio_cd = slot_config->gpio_cd; + int gpio_wp = slot_config->gpio_wp; + + // Configure pins + const sdmmc_slot_info_t* pslot = &s_slot_info[slot]; + configure_pin(pslot->clk); + configure_pin(pslot->cmd); + configure_pin(pslot->d0); + configure_pin(pslot->d1); + configure_pin(pslot->d2); + configure_pin(pslot->d3); + if (pslot->width == 8) { + configure_pin(pslot->d4); + configure_pin(pslot->d5); + configure_pin(pslot->d6); + configure_pin(pslot->d7); + } + if (gpio_cd != -1) { + gpio_set_direction(gpio_cd, GPIO_MODE_INPUT); + gpio_matrix_in(gpio_cd, pslot->card_detect, 0); + } + if (gpio_wp != -1) { + gpio_set_direction(gpio_wp, GPIO_MODE_INPUT); + gpio_matrix_in(gpio_wp, pslot->write_protect, 0); + } + // By default, set probing frequency (400kHz) and 1-bit bus + esp_err_t ret = sdmmc_host_set_card_clk(slot, 400); + if (ret != ESP_OK) { + return ret; + } + ret = sdmmc_host_set_bus_width(slot, 1); + if (ret != ESP_OK) { + return ret; + } + return ESP_OK; +} + +esp_err_t sdmmc_host_deinit() +{ + if (!s_intr_handle) { + return ESP_ERR_INVALID_STATE; + } + esp_intr_free(s_intr_handle); + s_intr_handle = NULL; + vQueueDelete(s_event_queue); + s_event_queue = NULL; + sdmmc_host_input_clk_disable(); + sdmmc_host_transaction_handler_deinit(); + return ESP_OK; +} + +esp_err_t sdmmc_host_wait_for_event(int tick_count, sdmmc_event_t* out_event) +{ + if (!out_event) { + return ESP_ERR_INVALID_ARG; + } + if (!s_event_queue) { + return ESP_ERR_INVALID_STATE; + } + int ret = xQueueReceive(s_event_queue, out_event, tick_count); + if (ret == pdFALSE) { + return ESP_ERR_TIMEOUT; + } + return ESP_OK; +} + +esp_err_t sdmmc_host_set_bus_width(int slot, size_t width) +{ + if (!(slot == 0 || slot == 1)) { + return ESP_ERR_INVALID_ARG; + } + if (s_slot_info[slot].width < width) { + return ESP_ERR_INVALID_ARG; + } + const uint16_t mask = BIT(slot); + if (width == 1) { + SDMMC.ctype.card_width_8 &= ~mask; + SDMMC.ctype.card_width &= ~mask; + } else if (width == 4) { + SDMMC.ctype.card_width_8 &= ~mask; + SDMMC.ctype.card_width |= mask; + } else if (width == 8){ + SDMMC.ctype.card_width_8 |= mask; + } else { + return ESP_ERR_INVALID_ARG; + } + ESP_LOGD(TAG, "slot=%d width=%d", slot, width); + return ESP_OK; +} + +static void sdmmc_host_dma_init() +{ + SDMMC.ctrl.dma_enable = 1; + SDMMC.bmod.val = 0; + SDMMC.bmod.sw_reset = 1; + SDMMC.idinten.ni = 1; + SDMMC.idinten.ri = 1; + SDMMC.idinten.ti = 1; +} + + +void sdmmc_host_dma_stop() +{ + SDMMC.ctrl.use_internal_dma = 0; + SDMMC.ctrl.dma_reset = 1; + SDMMC.bmod.fb = 0; + SDMMC.bmod.enable = 0; +} + +void sdmmc_host_dma_prepare(sdmmc_desc_t* desc, size_t block_size, size_t data_size) +{ + // TODO: set timeout depending on data size + SDMMC.tmout.val = 0xffffffff; + + // Set size of data and DMA descriptor pointer + SDMMC.bytcnt = data_size; + SDMMC.blksiz = block_size; + SDMMC.dbaddr = desc; + + // Enable everything needed to use DMA + SDMMC.ctrl.dma_enable = 1; + SDMMC.ctrl.use_internal_dma = 1; + SDMMC.bmod.enable = 1; + SDMMC.bmod.fb = 1; + sdmmc_host_dma_resume(); +} + +void sdmmc_host_dma_resume() +{ + SDMMC.pldmnd = 1; +} + +/** + * @brief SDMMC interrupt handler + * + * Ignoring SDIO and streaming read/writes for now (and considering just SD memory cards), + * all communication is driven by the master, and the hardware handles things like stop + * commands automatically. So the interrupt handler doesn't need to do much, we just push + * interrupt status into a queue, clear interrupt flags, and let the task currently doing + * communication figure out what to do next. + * + * Card detect interrupts pose a small issue though, because if a card is plugged in and + * out a few times, while there is no task to process the events, event queue can become + * full and some card detect events may be dropped. We ignore this problem for now, since + * the there are no other interesting events which can get lost due to this. + */ +static void sdmmc_isr(void* arg) { + QueueHandle_t queue = (QueueHandle_t) arg; + sdmmc_event_t event; + uint32_t pending = SDMMC.mintsts.val; + SDMMC.rintsts.val = pending; + event.sdmmc_status = pending; + + uint32_t dma_pending = SDMMC.idsts.val; + SDMMC.idsts.val = dma_pending; + event.dma_status = dma_pending & 0x1f; + + int higher_priority_task_awoken = pdFALSE; + xQueueSendFromISR(queue, &event, &higher_priority_task_awoken); + if (higher_priority_task_awoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + diff --git a/components/driver/sdmmc_private.h b/components/driver/sdmmc_private.h new file mode 100644 index 0000000000..5a10a3b672 --- /dev/null +++ b/components/driver/sdmmc_private.h @@ -0,0 +1,44 @@ +// Copyright 2015-2016 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 +#include +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "soc/sdmmc_struct.h" + +typedef struct { + uint32_t sdmmc_status; ///< masked SDMMC interrupt status + uint32_t dma_status; ///< masked DMA interrupt status +} sdmmc_event_t; + +void sdmmc_host_reset(); + +esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg); + +esp_err_t sdmmc_host_wait_for_event(int tick_count, sdmmc_event_t* out_event); + +void sdmmc_host_dma_prepare(sdmmc_desc_t* desc, size_t block_size, size_t data_size); + +void sdmmc_host_dma_stop(); + +void sdmmc_host_dma_resume(); + +esp_err_t sdmmc_host_transaction_handler_init(); + +void sdmmc_host_transaction_handler_deinit(); + diff --git a/components/driver/sdmmc_transaction.c b/components/driver/sdmmc_transaction.c new file mode 100644 index 0000000000..59b439eec9 --- /dev/null +++ b/components/driver/sdmmc_transaction.c @@ -0,0 +1,372 @@ +// Copyright 2015-2016 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. + +#include +#include "esp_err.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "soc/sdmmc_reg.h" +#include "soc/sdmmc_struct.h" +#include "driver/sdmmc_types.h" +#include "driver/sdmmc_defs.h" +#include "driver/sdmmc_host.h" +#include "sdmmc_private.h" + + +/* Number of DMA descriptors used for transfer. + * Increasing this value above 4 doesn't improve performance for the usual case + * of SD memory cards (most data transfers are multiples of 512 bytes). + */ +#define SDMMC_DMA_DESC_CNT 4 + +static const char* TAG = "sdmmc_req"; + +typedef enum { + SDMMC_IDLE, + SDMMC_SENDING_CMD, + SDMMC_SENDING_DATA, + SDMMC_BUSY, +} sdmmc_req_state_t; + +typedef struct { + uint8_t* ptr; + size_t size_remaining; + size_t next_desc; + size_t desc_remaining; +} sdmmc_transfer_state_t; + +const uint32_t SDMMC_DATA_ERR_MASK = + SDMMC_INTMASK_DTO | SDMMC_INTMASK_DCRC | + SDMMC_INTMASK_HTO | SDMMC_INTMASK_SBE | + SDMMC_INTMASK_EBE; + +const uint32_t SDMMC_DMA_DONE_MASK = + SDMMC_IDMAC_INTMASK_RI | SDMMC_IDMAC_INTMASK_TI | + SDMMC_IDMAC_INTMASK_NI; + +const uint32_t SDMMC_CMD_ERR_MASK = + SDMMC_INTMASK_RTO | + SDMMC_INTMASK_RCRC | + SDMMC_INTMASK_RESP_ERR; + +static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT]; +static sdmmc_transfer_state_t s_cur_transfer = { 0 }; +static QueueHandle_t s_request_mutex; + +static esp_err_t handle_idle_state_events(); +static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd); +static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* pstate); +static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_req_state_t* pstate); +static void process_command_response(uint32_t status, sdmmc_command_t* cmd); +static void fill_dma_descriptors(size_t num_desc); + +esp_err_t sdmmc_host_transaction_handler_init() +{ + assert(s_request_mutex == NULL); + s_request_mutex = xSemaphoreCreateMutex(); + if (!s_request_mutex) { + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +void sdmmc_host_transaction_handler_deinit() +{ + assert(s_request_mutex); + vSemaphoreDelete(s_request_mutex); + s_request_mutex = NULL; +} + +esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo) +{ + xSemaphoreTake(s_request_mutex, portMAX_DELAY); + // dispose of any events which happened asynchronously + handle_idle_state_events(); + // convert cmdinfo to hardware register value + sdmmc_hw_cmd_t hw_cmd = make_hw_cmd(cmdinfo); + if (cmdinfo->data) { + // these constraints should be handled by upper layer + assert(cmdinfo->datalen >= 4); + assert(cmdinfo->blklen % 4 == 0); + // this clears "owned by IDMAC" bits + memset(s_dma_desc, 0, sizeof(s_dma_desc)); + // initialize first descriptor + s_dma_desc[0].first_descriptor = 1; + // save transfer info + s_cur_transfer.ptr = (uint8_t*) cmdinfo->data; + s_cur_transfer.size_remaining = cmdinfo->datalen; + s_cur_transfer.next_desc = 0; + s_cur_transfer.desc_remaining = (cmdinfo->datalen + SDMMC_DMA_MAX_BUF_LEN - 1) / SDMMC_DMA_MAX_BUF_LEN; + // prepare descriptors + fill_dma_descriptors(SDMMC_DMA_DESC_CNT); + // write transfer info into hardware + sdmmc_host_dma_prepare(&s_dma_desc[0], cmdinfo->blklen, cmdinfo->datalen); + } + // write command into hardware, this also sends the command to the card + esp_err_t ret = sdmmc_host_start_command(slot, hw_cmd, cmdinfo->arg); + if (ret != ESP_OK) { + xSemaphoreGive(s_request_mutex); + return ret; + } + // process events until transfer is complete + cmdinfo->error = ESP_OK; + sdmmc_req_state_t state = SDMMC_SENDING_CMD; + while (state != SDMMC_IDLE) { + ret = handle_event(cmdinfo, &state); + if (ret != ESP_OK) { + break; + } + } + xSemaphoreGive(s_request_mutex); + return ret; +} + +static void fill_dma_descriptors(size_t num_desc) +{ + for (size_t i = 0; i < num_desc; ++i) { + if (s_cur_transfer.size_remaining == 0) { + return; + } + const size_t next = s_cur_transfer.next_desc; + sdmmc_desc_t* desc = &s_dma_desc[next]; + assert(!desc->owned_by_idmac); + size_t size_to_fill = + (s_cur_transfer.size_remaining < SDMMC_DMA_MAX_BUF_LEN) ? + s_cur_transfer.size_remaining : SDMMC_DMA_MAX_BUF_LEN; + bool last = size_to_fill == s_cur_transfer.size_remaining; + desc->last_descriptor = last; + desc->second_address_chained = 1; + desc->owned_by_idmac = 1; + desc->buffer1_ptr = s_cur_transfer.ptr; + desc->next_desc_ptr = (last) ? NULL : &s_dma_desc[(next + 1) % SDMMC_DMA_DESC_CNT]; + desc->buffer1_size = size_to_fill; + + s_cur_transfer.size_remaining -= size_to_fill; + s_cur_transfer.ptr += size_to_fill; + s_cur_transfer.next_desc = (s_cur_transfer.next_desc + 1) % SDMMC_DMA_DESC_CNT; + ESP_LOGV(TAG, "fill %d desc=%d rem=%d next=%d last=%d sz=%d", + num_desc, next, s_cur_transfer.size_remaining, + s_cur_transfer.next_desc, desc->last_descriptor, desc->buffer1_size); + } +} + +static esp_err_t handle_idle_state_events() +{ + /* Handle any events which have happened in between transfers. + * Under current assumptions (no SDIO support) only card detect events + * can happen in the idle state. + */ + sdmmc_event_t evt; + while (sdmmc_host_wait_for_event(0, &evt) == ESP_OK) { + if (evt.sdmmc_status & SDMMC_INTMASK_CD) { + ESP_LOGV(TAG, "card detect event"); + evt.sdmmc_status &= ~SDMMC_INTMASK_CD; + } + if (evt.sdmmc_status != 0 || evt.dma_status != 0) { + ESP_LOGE(TAG, "handle_idle_state_events unhandled: %08x %08x", + evt.sdmmc_status, evt.dma_status); + } + + } + return ESP_OK; +} + + +static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state) +{ + sdmmc_event_t evt; + esp_err_t err = sdmmc_host_wait_for_event(portMAX_DELAY, &evt); + if (err != ESP_OK) { + ESP_LOGE(TAG, "sdmmc_host_wait_for_event returned %d", err); + return err; + } + ESP_LOGV(TAG, "sdmmc_handle_event: evt %08x %08x", evt.sdmmc_status, evt.dma_status); + process_events(evt, cmd, state); + return ESP_OK; +} + +static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd) +{ + sdmmc_hw_cmd_t res = { 0 }; + + res.cmd_index = cmd->opcode; + if (cmd->opcode == MMC_STOP_TRANSMISSION) { + res.stop_abort_cmd = 1; + } else { + res.wait_complete = 1; + } + if (cmd->opcode == SD_APP_SET_BUS_WIDTH) { + res.send_auto_stop = 1; + res.data_expected = 1; + } + if (cmd->flags & SCF_RSP_PRESENT) { + res.response_expect = 1; + if (cmd->flags & SCF_RSP_136) { + res.response_long = 1; + } + } + if (cmd->flags & SCF_RSP_CRC) { + res.check_response_crc = 1; + } + res.use_hold_reg = 1; + if (cmd->data) { + res.data_expected = 1; + if ((cmd->flags & SCF_CMD_READ) == 0) { + res.rw = 1; + } + assert(cmd->datalen % cmd->blklen == 0); + if ((cmd->datalen / cmd->blklen) > 1) { + res.send_auto_stop = 1; + } + } + ESP_LOGV(TAG, "%s: opcode=%d, rexp=%d, crc=%d", __func__, + res.cmd_index, res.response_expect, res.check_response_crc); + return res; +} + +static void process_command_response(uint32_t status, sdmmc_command_t* cmd) +{ + if (cmd->flags & SCF_RSP_PRESENT) { + if (cmd->flags & SCF_RSP_136) { + cmd->response[3] = SDMMC.resp[0]; + cmd->response[2] = SDMMC.resp[1]; + cmd->response[1] = SDMMC.resp[2]; + cmd->response[0] = SDMMC.resp[3]; + + } else { + cmd->response[0] = SDMMC.resp[0]; + cmd->response[1] = 0; + cmd->response[2] = 0; + cmd->response[3] = 0; + } + } + + if ((status & SDMMC_INTMASK_RTO) && + cmd->opcode != MMC_ALL_SEND_CID && + cmd->opcode != MMC_SELECT_CARD && + cmd->opcode != MMC_STOP_TRANSMISSION) { + cmd->error = ESP_ERR_TIMEOUT; + } else if ((cmd->flags & SCF_RSP_CRC) && (status & SDMMC_INTMASK_RCRC)) { + cmd->error = ESP_ERR_INVALID_CRC; + } else if (status & SDMMC_INTMASK_RESP_ERR) { + cmd->error = ESP_ERR_INVALID_RESPONSE; + } + if (cmd->error != 0) { + if (cmd->data) { + sdmmc_host_dma_stop(); + } + ESP_LOGD(TAG, "%s: error %d", __func__, cmd->error); + } +} + +static void process_data_status(uint32_t status, sdmmc_command_t* cmd) +{ + if (status & SDMMC_DATA_ERR_MASK) { + if (status & SDMMC_INTMASK_DTO) { + cmd->error = ESP_ERR_TIMEOUT; + } else if (status & SDMMC_INTMASK_DCRC) { + cmd->error = ESP_ERR_INVALID_CRC; + } else if ((status & SDMMC_INTMASK_EBE) && + (cmd->flags & SCF_CMD_READ) == 0) { + cmd->error = ESP_ERR_TIMEOUT; + } else { + cmd->error = ESP_FAIL; + } + SDMMC.ctrl.fifo_reset = 1; + } + if (cmd->error != 0) { + if (cmd->data) { + sdmmc_host_dma_stop(); + } + ESP_LOGD(TAG, "%s: error %d", __func__, cmd->error); + } + +} + +static inline bool mask_check_and_clear(uint32_t* state, uint32_t mask) { + bool ret = ((*state) & mask) != 0; + *state &= ~mask; + return ret; +} + +static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_req_state_t* pstate) +{ + const char* const s_state_names[] __attribute__((unused)) = { + "IDLE", + "SENDING_CMD", + "SENDIND_DATA", + "BUSY" + }; + sdmmc_event_t orig_evt = evt; + ESP_LOGV(TAG, "%s: state=%s", __func__, s_state_names[*pstate]); + sdmmc_req_state_t next_state = *pstate; + sdmmc_req_state_t state = (sdmmc_req_state_t) -1; + while (next_state != state) { + state = next_state; + switch (state) { + case SDMMC_IDLE: + break; + + case SDMMC_SENDING_CMD: + if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_CMD_ERR_MASK)) { + process_command_response(orig_evt.sdmmc_status, cmd); + break; + } + if (!mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_CMD_DONE)) { + break; + } + process_command_response(orig_evt.sdmmc_status, cmd); + if (cmd->error != ESP_OK || cmd->data == NULL) { + next_state = SDMMC_IDLE; + break; + } + next_state = SDMMC_SENDING_DATA; + break; + + + case SDMMC_SENDING_DATA: + if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_DATA_ERR_MASK)) { + process_data_status(orig_evt.sdmmc_status, cmd); + sdmmc_host_dma_stop(); + } + if (mask_check_and_clear(&evt.dma_status, SDMMC_DMA_DONE_MASK)) { + s_cur_transfer.desc_remaining--; + if (s_cur_transfer.size_remaining) { + fill_dma_descriptors(1); + sdmmc_host_dma_resume(); + } + if (s_cur_transfer.desc_remaining == 0) { + next_state = SDMMC_BUSY; + } + } + break; + + case SDMMC_BUSY: + if (!mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_DATA_OVER)) { + break; + } + process_data_status(orig_evt.sdmmc_status, cmd); + next_state = SDMMC_IDLE; + break; + } + ESP_LOGV(TAG, "%s state=%s next_state=%s", __func__, s_state_names[state], s_state_names[next_state]); + } + *pstate = state; + return ESP_OK; +} + + + diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c new file mode 100644 index 0000000000..6162edba36 --- /dev/null +++ b/components/driver/spi_master.c @@ -0,0 +1,705 @@ +// Copyright 2015-2016 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. + +/* +Architecture: + +We can initialize a SPI driver, but we don't talk to the SPI driver itself, we address a device. A device essentially +is a combination of SPI port and CS pin, plus some information about the specifics of communication to the device +(timing, command/address length etc) + +The essence of the interface to a device is a set of queues; one per device. The idea is that to send something to a SPI +device, you allocate a transaction descriptor. It contains some information about the transfer like the lenghth, address, +command etc, plus pointers to transmit and receive buffer. The address of this block gets pushed into the transmit queue. +The SPI driver does its magic, and sends and retrieves the data eventually. The data gets written to the receive buffers, +if needed the transaction descriptor is modified to indicate returned parameters and the entire thing goes into the return +queue, where whatever software initiated the transaction can retrieve it. + +The entire thing is run from the SPI interrupt handler. If SPI is done transmitting/receiving but nothing is in the queue, +it will not clear the SPI interrupt but just disable it. This way, when a new thing is sent, pushing the packet into the send +queue and re-enabling the interrupt will trigger the interrupt again, which can then take care of the sending. +*/ + + + +#include +#include "driver/spi_master.h" +#include "soc/gpio_sig_map.h" +#include "soc/spi_reg.h" +#include "soc/dport_reg.h" +#include "soc/spi_struct.h" +#include "soc/rtc_cntl_reg.h" +#include "rom/ets_sys.h" +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_intr_alloc.h" +#include "esp_log.h" +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/soc.h" +#include "soc/dport_reg.h" +#include "soc/uart_struct.h" +#include "rom/lldesc.h" +#include "driver/uart.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" +#include "esp_heap_alloc_caps.h" + +typedef struct spi_device_t spi_device_t; + +#define NO_CS 3 //Number of CS pins per SPI host + +typedef struct { + spi_device_t *device[NO_CS]; + intr_handle_t intr; + spi_dev_t *hw; + spi_transaction_t *cur_trans; + int cur_cs; + lldesc_t dmadesc_tx, dmadesc_rx; + bool no_gpio_matrix; +} spi_host_t; + +struct spi_device_t { + QueueHandle_t trans_queue; + QueueHandle_t ret_queue; + spi_device_interface_config_t cfg; + spi_host_t *host; +}; + +static spi_host_t *spihost[3]; + + +static const char *SPI_TAG = "spi_master"; +#define SPI_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(SPI_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } + +/* + Stores a bunch of per-spi-peripheral data. +*/ +typedef struct { + const uint8_t spiclk_out; //GPIO mux output signals + const uint8_t spid_out; + const uint8_t spiq_out; + const uint8_t spiwp_out; + const uint8_t spihd_out; + const uint8_t spid_in; //GPIO mux input signals + const uint8_t spiq_in; + const uint8_t spiwp_in; + const uint8_t spihd_in; + const uint8_t spics_out[3]; // /CS GPIO output mux signals + const uint8_t spiclk_native; //IO pins of IO_MUX muxed signals + const uint8_t spid_native; + const uint8_t spiq_native; + const uint8_t spiwp_native; + const uint8_t spihd_native; + const uint8_t spics0_native; + const uint8_t irq; //irq source for interrupt mux + const uint8_t irq_dma; //dma irq source for interrupt mux + const periph_module_t module; //peripheral module, for enabling clock etc + spi_dev_t *hw; //Pointer to the hardware registers +} spi_signal_conn_t; + +/* + Bunch of constants for every SPI peripheral: GPIO signals, irqs, hw addr of registers etc +*/ +static const spi_signal_conn_t io_signal[3]={ + { + .spiclk_out=SPICLK_OUT_IDX, + .spid_out=SPID_OUT_IDX, + .spiq_out=SPIQ_OUT_IDX, + .spiwp_out=SPIWP_OUT_IDX, + .spihd_out=SPIHD_OUT_IDX, + .spid_in=SPID_IN_IDX, + .spiq_in=SPIQ_IN_IDX, + .spiwp_in=SPIWP_IN_IDX, + .spihd_in=SPIHD_IN_IDX, + .spics_out={SPICS0_OUT_IDX, SPICS1_OUT_IDX, SPICS2_OUT_IDX}, + .spiclk_native=6, + .spid_native=8, + .spiq_native=7, + .spiwp_native=10, + .spihd_native=9, + .spics0_native=11, + .irq=ETS_SPI1_INTR_SOURCE, + .irq_dma=ETS_SPI1_DMA_INTR_SOURCE, + .module=PERIPH_SPI_MODULE, + .hw=&SPI1 + }, { + .spiclk_out=HSPICLK_OUT_IDX, + .spid_out=HSPID_OUT_IDX, + .spiq_out=HSPIQ_OUT_IDX, + .spiwp_out=HSPIWP_OUT_IDX, + .spihd_out=HSPIHD_OUT_IDX, + .spid_in=HSPID_IN_IDX, + .spiq_in=HSPIQ_IN_IDX, + .spiwp_in=HSPIWP_IN_IDX, + .spihd_in=HSPIHD_IN_IDX, + .spics_out={HSPICS0_OUT_IDX, HSPICS1_OUT_IDX, HSPICS2_OUT_IDX}, + .spiclk_native=14, + .spid_native=13, + .spiq_native=12, + .spiwp_native=2, + .spihd_native=4, + .spics0_native=15, + .irq=ETS_SPI2_INTR_SOURCE, + .irq_dma=ETS_SPI2_DMA_INTR_SOURCE, + .module=PERIPH_HSPI_MODULE, + .hw=&SPI2 + }, { + .spiclk_out=VSPICLK_OUT_IDX, + .spid_out=VSPID_OUT_IDX, + .spiq_out=VSPIQ_OUT_IDX, + .spiwp_out=VSPIWP_OUT_IDX, + .spihd_out=VSPIHD_OUT_IDX, + .spid_in=VSPID_IN_IDX, + .spiq_in=VSPIQ_IN_IDX, + .spiwp_in=VSPIWP_IN_IDX, + .spihd_in=VSPIHD_IN_IDX, + .spics_out={VSPICS0_OUT_IDX, VSPICS1_OUT_IDX, VSPICS2_OUT_IDX}, + .spiclk_native=18, + .spid_native=23, + .spiq_native=19, + .spiwp_native=22, + .spihd_native=21, + .spics0_native=5, + .irq=ETS_SPI3_INTR_SOURCE, + .irq_dma=ETS_SPI3_DMA_INTR_SOURCE, + .module=PERIPH_VSPI_MODULE, + .hw=&SPI3 + } +}; + +static void spi_intr(void *arg); + + +esp_err_t spi_bus_initialize(spi_host_device_t host, spi_bus_config_t *bus_config, int dma_chan) +{ + bool native=true; + /* ToDo: remove this when we have flash operations cooperating with this */ + SPI_CHECK(host!=SPI_HOST, "SPI1 is not supported", ESP_ERR_NOT_SUPPORTED); + + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK(spihost[host]==NULL, "host already in use", ESP_ERR_INVALID_STATE); + + SPI_CHECK(bus_config->mosi_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->mosi_io_num), "spid pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->sclk_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->sclk_io_num), "spiclk pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->miso_io_num<0 || GPIO_IS_VALID_GPIO(bus_config->miso_io_num), "spiq pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->quadwp_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadwp_io_num), "spiwp pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->quadhd_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadhd_io_num), "spihd pin invalid", ESP_ERR_INVALID_ARG); + + //The host struct contains two dma descriptors, so we need DMA'able memory for this. + spihost[host]=pvPortMallocCaps(sizeof(spi_host_t), MALLOC_CAP_DMA); + if (spihost[host]==NULL) return ESP_ERR_NO_MEM; + memset(spihost[host], 0, sizeof(spi_host_t)); + + //Check if the selected pins correspond to the native pins of the peripheral + if (bus_config->mosi_io_num >= 0 && bus_config->mosi_io_num!=io_signal[host].spid_native) native=false; + if (bus_config->miso_io_num >= 0 && bus_config->miso_io_num!=io_signal[host].spiq_native) native=false; + if (bus_config->sclk_io_num >= 0 && bus_config->sclk_io_num!=io_signal[host].spiclk_native) native=false; + if (bus_config->quadwp_io_num >= 0 && bus_config->quadwp_io_num!=io_signal[host].spiwp_native) native=false; + if (bus_config->quadhd_io_num >= 0 && bus_config->quadhd_io_num!=io_signal[host].spihd_native) native=false; + + spihost[host]->no_gpio_matrix=native; + if (native) { + //All SPI native pin selections resolve to 1, so we put that here instead of trying to figure + //out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway. + if (bus_config->mosi_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], 1); + if (bus_config->miso_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], 1); + if (bus_config->quadwp_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], 1); + if (bus_config->quadhd_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], 1); + if (bus_config->sclk_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], 1); + } else { + //Use GPIO + if (bus_config->mosi_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->mosi_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->mosi_io_num, io_signal[host].spid_out, false, false); + gpio_matrix_in(bus_config->mosi_io_num, io_signal[host].spid_in, false); + } + if (bus_config->miso_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->miso_io_num, GPIO_MODE_INPUT); + gpio_matrix_out(bus_config->miso_io_num, io_signal[host].spiq_out, false, false); + gpio_matrix_in(bus_config->miso_io_num, io_signal[host].spiq_in, false); + } + if (bus_config->quadwp_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->quadwp_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->quadwp_io_num, io_signal[host].spiwp_out, false, false); + gpio_matrix_in(bus_config->quadwp_io_num, io_signal[host].spiwp_in, false); + } + if (bus_config->quadhd_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->quadhd_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->quadhd_io_num, io_signal[host].spihd_out, false, false); + gpio_matrix_in(bus_config->quadhd_io_num, io_signal[host].spihd_in, false); + } + if (bus_config->sclk_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->sclk_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->sclk_io_num, io_signal[host].spiclk_out, false, false); + } + } + periph_module_enable(io_signal[host].module); + esp_intr_alloc(io_signal[host].irq, ESP_INTR_FLAG_INTRDISABLED, spi_intr, (void*)spihost[host], &spihost[host]->intr); + spihost[host]->hw=io_signal[host].hw; + + //Reset DMA + spihost[host]->hw->dma_conf.val|=SPI_OUT_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + spihost[host]->hw->dma_out_link.start=0; + spihost[host]->hw->dma_in_link.start=0; + spihost[host]->hw->dma_conf.val&=~(SPI_OUT_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + + //Disable unneeded ints + spihost[host]->hw->slave.rd_buf_done=0; + spihost[host]->hw->slave.wr_buf_done=0; + spihost[host]->hw->slave.rd_sta_done=0; + spihost[host]->hw->slave.wr_sta_done=0; + spihost[host]->hw->slave.rd_buf_inten=0; + spihost[host]->hw->slave.wr_buf_inten=0; + spihost[host]->hw->slave.rd_sta_inten=0; + spihost[host]->hw->slave.wr_sta_inten=0; + + //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as + //disabled. This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling + //any transactions that are queued. + spihost[host]->hw->slave.trans_inten=1; + spihost[host]->hw->slave.trans_done=1; + + //Select DMA channel. + SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, dma_chan, (host * 2)); + + return ESP_OK; +} + +esp_err_t spi_bus_free(spi_host_device_t host) +{ + int x; + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK(spihost[host]!=NULL, "host not in use", ESP_ERR_INVALID_STATE); + for (x=0; xdevice[x]==NULL, "not all CSses freed", ESP_ERR_INVALID_STATE); + } + spihost[host]->hw->slave.trans_inten=0; + spihost[host]->hw->slave.trans_done=0; + esp_intr_free(spihost[host]->intr); + periph_module_disable(io_signal[host].module); + free(spihost[host]); + spihost[host]=NULL; + return ESP_OK; +} + +/* + Add a device. This allocates a CS line for the device, allocates memory for the device structure and hooks + up the CS pin to whatever is specified. +*/ +esp_err_t spi_bus_add_device(spi_host_device_t host, spi_device_interface_config_t *dev_config, spi_device_handle_t *handle) +{ + int freecs; + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK(spihost[host]!=NULL, "host not initialized", ESP_ERR_INVALID_STATE); + SPI_CHECK(dev_config->spics_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num), "spics pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(dev_config->clock_speed_hz > 0, "invalid sclk speed", ESP_ERR_INVALID_ARG); + for (freecs=0; freecsdevice[freecs], NULL, (spi_device_t *)1)) break; + } + SPI_CHECK(freecs!=NO_CS, "no free cs pins for host", ESP_ERR_NOT_FOUND); + //The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full + //duplex mode does absolutely nothing on the ESP32. + SPI_CHECK(dev_config->cs_ena_pretrans==0 || (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "cs pretrans delay incompatible with full-duplex", ESP_ERR_INVALID_ARG); + + //Allocate memory for device + spi_device_t *dev=malloc(sizeof(spi_device_t)); + if (dev==NULL) return ESP_ERR_NO_MEM; + memset(dev, 0, sizeof(spi_device_t)); + spihost[host]->device[freecs]=dev; + + //Allocate queues, set defaults + dev->trans_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_transaction_t *)); + dev->ret_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_transaction_t *)); + if (dev_config->duty_cycle_pos==0) dev_config->duty_cycle_pos=128; + dev->host=spihost[host]; + + //We want to save a copy of the dev config in the dev struct. + memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t)); + + //Set CS pin, CS options + if (dev_config->spics_io_num > 0) { + if (spihost[host]->no_gpio_matrix &&dev_config->spics_io_num == io_signal[host].spics0_native && freecs==0) { + //Again, the cs0s for all SPI peripherals map to pin mux source 1, so we use that instead of a define. + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], 1); + } else { + //Use GPIO matrix + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], PIN_FUNC_GPIO); + gpio_set_direction(dev_config->spics_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(dev_config->spics_io_num, io_signal[host].spics_out[freecs], false, false); + } + } + if (dev_config->flags&SPI_DEVICE_CLK_AS_CS) { + spihost[host]->hw->pin.master_ck_sel |= (1<hw->pin.master_ck_sel &= (1<flags&SPI_DEVICE_POSITIVE_CS) { + spihost[host]->hw->pin.master_cs_pol |= (1<hw->pin.master_cs_pol &= (1<trans_queue)==0, "Have unfinished transactions", ESP_ERR_INVALID_STATE); + SPI_CHECK(handle->host->cur_trans==0 || handle->host->device[handle->host->cur_cs]!=handle, "Have unfinished transactions", ESP_ERR_INVALID_STATE); + SPI_CHECK(uxQueueMessagesWaiting(handle->ret_queue)==0, "Have unfinished transactions", ESP_ERR_INVALID_STATE); + + //Kill queues + vQueueDelete(handle->trans_queue); + vQueueDelete(handle->ret_queue); + //Remove device from list of csses and free memory + for (x=0; xhost->device[x] == handle) handle->host->device[x]=NULL; + } + free(handle); + return ESP_OK; +} + +static int spi_freq_for_pre_n(int fapb, int pre, int n) { + return (fapb / (pre * n)); +} + +static void spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) { + int pre, n, h, l; + + //In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value. + if (hz>((fapb/4)*3)) { + //Using Fapb directly will give us the best result here. + hw->clock.clkcnt_l=0; + hw->clock.clkcnt_h=0; + hw->clock.clkcnt_n=0; + hw->clock.clkdiv_pre=0; + hw->clock.clk_equ_sysclk=1; + } else { + //For best duty cycle resolution, we want n to be as close to 32 as possible, but + //we also need a pre/n combo that gets us as close as possible to the intended freq. + //To do this, we bruteforce n and calculate the best pre to go along with that. + //If there's a choice between pre/n combos that give the same result, use the one + //with the higher n. + int bestn=-1; + int bestpre=-1; + int besterr=0; + int errval; + for (n=1; n<=64; n++) { + //Effectively, this does pre=round((fapb/n)/hz). + pre=((fapb/n)+(hz/2))/hz; + if (pre<=0) pre=1; + if (pre>8192) pre=8192; + errval=abs(spi_freq_for_pre_n(fapb, pre, n)-hz); + if (bestn==-1 || errval<=besterr) { + besterr=errval; + bestn=n; + bestpre=pre; + } + } + + n=bestn; + pre=bestpre; + l=n; + //This effectively does round((duty_cycle*n)/256) + h=(duty_cycle*n+127)/256; + if (h<=0) h=1; + + hw->clock.clk_equ_sysclk=0; + hw->clock.clkcnt_n=n-1; + hw->clock.clkdiv_pre=pre-1; + hw->clock.clkcnt_h=h-1; + hw->clock.clkcnt_l=l-1; + } +} + + +//If a transaction is smaller than or equal to of bits, we do not use DMA; instead, we directly copy/paste +//bits from/to the work registers. Keep between 32 and (8*32) please. +#define THRESH_DMA_TRANS (8*32) + +//This is run in interrupt context and apart from initialization and destruction, this is the only code +//touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are +//no muxes in this code. +static void IRAM_ATTR spi_intr(void *arg) +{ + int i; + int prevCs=-1; + BaseType_t r; + BaseType_t do_yield=pdFALSE; + spi_transaction_t *trans=NULL; + spi_host_t *host=(spi_host_t*)arg; + + //Ignore all but the trans_done int. + if (!host->hw->slave.trans_done) return; + + if (host->cur_trans) { + //Okay, transaction is done. + if ((host->cur_trans->rx_buffer || (host->cur_trans->flags & SPI_TRANS_USE_RXDATA)) && host->cur_trans->rxlength<=THRESH_DMA_TRANS) { + //Need to copy from SPI regs to result buffer. + uint32_t *data; + if (host->cur_trans->flags & SPI_TRANS_USE_RXDATA) { + data=(uint32_t*)&host->cur_trans->rx_data[0]; + } else { + data=(uint32_t*)host->cur_trans->rx_buffer; + } + for (int x=0; x < host->cur_trans->rxlength; x+=32) { + //Do a memcpy to get around possible alignment issues in rx_buffer + uint32_t word=host->hw->data_buf[x/32]; + memcpy(&data[x/32], &word, 4); + } + } + //Call post-transaction callback, if any + if (host->device[host->cur_cs]->cfg.post_cb) host->device[host->cur_cs]->cfg.post_cb(host->cur_trans); + //Return transaction descriptor. + xQueueSendFromISR(host->device[host->cur_cs]->ret_queue, &host->cur_trans, &do_yield); + host->cur_trans=NULL; + prevCs=host->cur_cs; + } + //ToDo: This is a stupidly simple low-cs-first priority scheme. Make this configurable somehow. - JD + for (i=0; idevice[i]) { + r=xQueueReceiveFromISR(host->device[i]->trans_queue, &trans, &do_yield); + //Stop looking if we have a transaction to send. + if (r) break; + } + } + if (i==NO_CS) { + //No packet waiting. Disable interrupt. + esp_intr_disable(host->intr); + } else { + host->hw->slave.trans_done=0; //clear int bit + //We have a transaction. Send it. + spi_device_t *dev=host->device[i]; + host->cur_trans=trans; + //We should be done with the transmission. + assert(host->hw->cmd.usr == 0); + + //Default rxlength to be the same as length, if not filled in. + if (trans->rxlength==0) { + trans->rxlength=trans->length; + } + + //Reconfigure accoding to device settings, but only if we change CSses. + if (i!=prevCs) { + //Assumes a hardcoded 80MHz Fapb for now. ToDo: figure out something better once we have + //clock scaling working. + int apbclk=APB_CLK_FREQ; + spi_set_clock(host->hw, apbclk, dev->cfg.clock_speed_hz, dev->cfg.duty_cycle_pos); + //Configure bit order + host->hw->ctrl.rd_bit_order=(dev->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST)?1:0; + host->hw->ctrl.wr_bit_order=(dev->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST)?1:0; + + //Configure polarity + //SPI iface needs to be configured for a delay unless it is not routed through GPIO and clock is >=apb/2 + int nodelay=(host->no_gpio_matrix && dev->cfg.clock_speed_hz >= (apbclk/2)); + if (dev->cfg.mode==0) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } else if (dev->cfg.mode==1) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (dev->cfg.mode==2) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (dev->cfg.mode==3) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } + + //Configure bit sizes, load addr and command + host->hw->user.usr_dummy=(dev->cfg.dummy_bits)?1:0; + host->hw->user.usr_addr=(dev->cfg.address_bits)?1:0; + host->hw->user.usr_command=(dev->cfg.command_bits)?1:0; + host->hw->user1.usr_addr_bitlen=dev->cfg.address_bits-1; + host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits-1; + host->hw->user2.usr_command_bitlen=dev->cfg.command_bits-1; + //Configure misc stuff + host->hw->user.doutdin=(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1; + host->hw->user.sio=(dev->cfg.flags & SPI_DEVICE_3WIRE)?1:0; + + host->hw->ctrl2.setup_time=dev->cfg.cs_ena_pretrans-1; + host->hw->user.cs_setup=dev->cfg.cs_ena_pretrans?1:0; + host->hw->ctrl2.hold_time=dev->cfg.cs_ena_posttrans-1; + host->hw->user.cs_hold=(dev->cfg.cs_ena_posttrans)?1:0; + + //Configure CS pin + host->hw->pin.cs0_dis=(i==0)?0:1; + host->hw->pin.cs1_dis=(i==1)?0:1; + host->hw->pin.cs2_dis=(i==2)?0:1; + } + //Reset DMA + host->hw->dma_conf.val |= SPI_OUT_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + host->hw->dma_out_link.start=0; + host->hw->dma_in_link.start=0; + host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + //QIO/DIO + host->hw->ctrl.val &= ~(SPI_FREAD_DUAL|SPI_FREAD_QUAD|SPI_FREAD_DIO|SPI_FREAD_QIO); + host->hw->user.val &= ~(SPI_FWRITE_DUAL|SPI_FWRITE_QUAD|SPI_FWRITE_DIO|SPI_FWRITE_QIO); + if (trans->flags & SPI_TRANS_MODE_DIO) { + if (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR) { + host->hw->ctrl.fread_dio=1; + host->hw->user.fwrite_dio=1; + } else { + host->hw->ctrl.fread_dual=1; + host->hw->user.fwrite_dual=1; + } + host->hw->ctrl.fastrd_mode=1; + } else if (trans->flags & SPI_TRANS_MODE_QIO) { + if (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR) { + host->hw->ctrl.fread_qio=1; + host->hw->user.fwrite_qio=1; + } else { + host->hw->ctrl.fread_quad=1; + host->hw->user.fwrite_quad=1; + } + host->hw->ctrl.fastrd_mode=1; + } + + + //Fill DMA descriptors + if (trans->rx_buffer || (trans->flags & SPI_TRANS_USE_RXDATA)) { + uint32_t *data; + if (trans->flags & SPI_TRANS_USE_RXDATA) { + data=(uint32_t *)&trans->rx_data[0]; + } else { + data=trans->rx_buffer; + } + if (trans->rxlengthhw->user.usr_miso_highpart=0; + host->dmadesc_rx.size=(trans->rxlength+7)/8; + host->dmadesc_rx.length=(trans->rxlength+7)/8; + host->dmadesc_rx.buf=(uint8_t*)data; + host->dmadesc_rx.eof=1; + host->dmadesc_rx.sosf=0; + host->dmadesc_rx.owner=1; + host->hw->dma_in_link.addr=(int)(&host->dmadesc_rx)&0xFFFFF; + host->hw->dma_in_link.start=1; + } + host->hw->user.usr_miso=1; + } else { + host->hw->user.usr_miso=0; + } + + if (trans->tx_buffer || (trans->flags & SPI_TRANS_USE_TXDATA)) { + uint32_t *data; + if (trans->flags & SPI_TRANS_USE_TXDATA) { + data=(uint32_t *)&trans->tx_data[0]; + } else { + data=(uint32_t *)trans->tx_buffer; + } + if (trans->rxlength < 8*32) { + //No need for DMA. + for (int x=0; x < trans->rxlength; x+=32) { + //Use memcpy to get around alignment issues for txdata + uint32_t word; + memcpy(&word, &data[x/32], 4); + host->hw->data_buf[(x/32)+8]=word; + } + host->hw->user.usr_mosi_highpart=1; + } else { + host->hw->user.usr_mosi_highpart=0; + host->dmadesc_tx.size=(trans->length+7)/8; + host->dmadesc_tx.length=(trans->length+7)/8; + host->dmadesc_tx.buf=(uint8_t*)data; + host->dmadesc_tx.eof=1; + host->dmadesc_tx.sosf=0; + host->dmadesc_tx.owner=1; + host->hw->dma_out_link.addr=(int)(&host->dmadesc_tx) & 0xFFFFF; + host->hw->dma_out_link.start=1; + } + } + host->hw->mosi_dlen.usr_mosi_dbitlen=trans->length-1; + host->hw->miso_dlen.usr_miso_dbitlen=trans->rxlength-1; + host->hw->user2.usr_command_value=trans->command; + if (dev->cfg.address_bits>32) { + host->hw->addr=trans->address >> 32; + host->hw->slv_wr_status=trans->address & 0xffffffff; + } else { + host->hw->addr=trans->address & 0xffffffff; + } + host->hw->user.usr_mosi=(trans->tx_buffer==NULL)?0:1; + host->hw->user.usr_miso=(trans->tx_buffer==NULL)?0:1; + + //Call pre-transmission callback, if any + if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans); + //Kick off transfer + host->hw->cmd.usr=1; + } + if (do_yield) portYIELD_FROM_ISR(); +} + + +esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait) +{ + BaseType_t r; + SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG); + SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0 ||trans_desc->length <= 32, "rxdata transfer > 32bytes", ESP_ERR_INVALID_ARG); + SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_TXDATA)==0 ||trans_desc->length <= 32, "txdata transfer > 32bytes", ESP_ERR_INVALID_ARG); + SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG); + SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX))), "incompatible iface params", ESP_ERR_INVALID_ARG); + r=xQueueSend(handle->trans_queue, (void*)&trans_desc, ticks_to_wait); + if (!r) return ESP_ERR_TIMEOUT; + esp_intr_enable(handle->host->intr); + return ESP_OK; +} + +esp_err_t spi_device_get_trans_result(spi_device_handle_t handle, spi_transaction_t **trans_desc, TickType_t ticks_to_wait) +{ + BaseType_t r; + SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG); + r=xQueueReceive(handle->ret_queue, (void*)trans_desc, ticks_to_wait); + if (!r) return ESP_ERR_TIMEOUT; + return ESP_OK; +} + +//Porcelain to do one blocking transmission. +esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *trans_desc) +{ + esp_err_t ret; + spi_transaction_t *ret_trans; + //ToDo: check if any spi transfers in flight + ret=spi_device_queue_trans(handle, trans_desc, portMAX_DELAY); + if (ret!=ESP_OK) return ret; + ret=spi_device_get_trans_result(handle, &ret_trans, portMAX_DELAY); + if (ret!=ESP_OK) return ret; + assert(ret_trans==trans_desc); + return ESP_OK; +} + diff --git a/components/driver/test/component.mk b/components/driver/test/component.mk new file mode 100644 index 0000000000..5dd172bdb7 --- /dev/null +++ b/components/driver/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/driver/test/test_spi_master.c b/components/driver/test/test_spi_master.c new file mode 100644 index 0000000000..d54cc5a633 --- /dev/null +++ b/components/driver/test/test_spi_master.c @@ -0,0 +1,140 @@ +/* + Tests for the spi_master device driver +*/ + +#include +#include +#include +#include +#include +#include "rom/ets_sys.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "driver/spi_master.h" +#include "soc/dport_reg.h" +#include "soc/spi_reg.h" +#include "soc/spi_struct.h" + + +static void check_spi_pre_n_for(int clk, int pre, int n) +{ + esp_err_t ret; + spi_device_handle_t handle; + + spi_device_interface_config_t devcfg={ + .command_bits=0, + .address_bits=0, + .dummy_bits=0, + .clock_speed_hz=clk, + .duty_cycle_pos=128, + .mode=0, + .spics_io_num=21, + .queue_size=3 + }; + char sendbuf[16]=""; + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + + ret=spi_bus_add_device(HSPI_HOST, &devcfg, &handle); + TEST_ASSERT(ret==ESP_OK); + + t.length=16*8; + t.tx_buffer=sendbuf; + ret=spi_device_transmit(handle, &t); + + printf("Checking clk rate %dHz. expect pre %d n %d, got pre %d n %d\n", clk, pre, n, SPI2.clock.clkdiv_pre+1, SPI2.clock.clkcnt_n+1); + + TEST_ASSERT(SPI2.clock.clkcnt_n+1==n); + TEST_ASSERT(SPI2.clock.clkdiv_pre+1==pre); + + ret=spi_bus_remove_device(handle); + TEST_ASSERT(ret==ESP_OK); +} + + +TEST_CASE("SPI Master clockdiv calculation routines", "[spi]") +{ + spi_bus_config_t buscfg={ + .mosi_io_num=4, + .miso_io_num=16, + .sclk_io_num=25, + .quadwp_io_num=-1, + .quadhd_io_num=-1 + }; + esp_err_t ret; + ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1); + TEST_ASSERT(ret==ESP_OK); + + check_spi_pre_n_for(8000000, 1, 10); + check_spi_pre_n_for(800000, 2, 50); + check_spi_pre_n_for(100000, 16, 50); + check_spi_pre_n_for(333333, 4, 60); + check_spi_pre_n_for(1, 8192, 64); //Actually should generate the minimum clock speed, 152Hz + + ret=spi_bus_free(HSPI_HOST); + TEST_ASSERT(ret==ESP_OK); +} + + +TEST_CASE("SPI Master test", "[spi]") +{ + spi_bus_config_t buscfg={ + .mosi_io_num=4, + .miso_io_num=16, + .sclk_io_num=25, + .quadwp_io_num=-1, + .quadhd_io_num=-1 + }; + spi_device_interface_config_t devcfg={ + .command_bits=8, + .address_bits=64, + .dummy_bits=0, + .clock_speed_hz=8000, + .duty_cycle_pos=128, + .mode=0, + .spics_io_num=21, + .queue_size=3 + }; + + esp_err_t ret; + spi_device_handle_t handle; + printf("THIS TEST NEEDS A JUMPER BETWEEN IO4 AND IO16\n"); + + ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1); + TEST_ASSERT(ret==ESP_OK); + ret=spi_bus_add_device(HSPI_HOST, &devcfg, &handle); + TEST_ASSERT(ret==ESP_OK); + printf("Bus/dev inited.\n"); + spi_transaction_t t; + char sendbuf[16]="Hello World!"; + char recvbuf[16]="UUUUUUUUUUUUUUU"; + memset(&t, 0, sizeof(t)); + + t.length=16*8; + t.tx_buffer=sendbuf; + t.rx_buffer=recvbuf; + t.address=0xA00000000000000FL; + t.command=0x55; + printf("Transmit...\n"); + ret=spi_device_transmit(handle, &t); + TEST_ASSERT(ret==ESP_OK); + printf("Send vs recv:\n"); + for (int x=0; x<16; x++) printf("%02X ", (int)sendbuf[x]); + printf("= 0) { PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[rx_io_num], PIN_FUNC_GPIO); + gpio_set_pull_mode(rx_io_num, GPIO_PULLUP_ONLY); gpio_set_direction(rx_io_num, GPIO_MODE_INPUT); gpio_matrix_in(rx_io_num, rx_sig, 0); } @@ -397,6 +398,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r } if(cts_io_num >= 0) { PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[cts_io_num], PIN_FUNC_GPIO); + gpio_set_pull_mode(cts_io_num, GPIO_PULLUP_ONLY); gpio_set_direction(cts_io_num, GPIO_MODE_INPUT); gpio_matrix_in(cts_io_num, cts_sig, 0); } @@ -464,7 +466,7 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ } UART[uart_num]->int_ena.val = intr_conf->intr_enable_mask; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - return ESP_FAIL; + return ESP_OK; } //internal isr handler for default driver code. @@ -639,10 +641,10 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.brk_det = 1; uart_event.type = UART_BREAK; } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { - uart_reg->int_clr.parity_err = 1; + uart_reg->int_clr.frm_err = 1; uart_event.type = UART_FRAME_ERR; } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) { - uart_reg->int_clr.frm_err = 1; + uart_reg->int_clr.parity_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); @@ -950,7 +952,7 @@ esp_err_t uart_flush(uart_port_t uart_num) return ESP_OK; } -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags) +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_CHECK((rx_buffer_size > UART_FIFO_LEN), "uart rx buffer length error(>128)", ESP_FAIL); @@ -978,7 +980,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b if(uart_queue) { p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); - *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; + *uart_queue = p_uart_obj[uart_num]->xQueueUart; ESP_LOGI(UART_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); } else { p_uart_obj[uart_num]->xQueueUart = NULL; diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 6ee5313b9b..0afe5ba0f8 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -20,41 +20,6 @@ config ESP32_DEFAULT_CPU_FREQ_MHZ default 160 if ESP32_DEFAULT_CPU_FREQ_160 default 240 if ESP32_DEFAULT_CPU_FREQ_240 -#choice ESP32_WIFI_OR_BT -# prompt "Select stack to enable (WiFi or BT)" -# default ESP32_ENABLE_WIFI -# help -# Temporarily, WiFi and BT stacks can not be used at the same time. -# Select which stack to enable. - -config ESP32_ENABLE_STACK_WIFI - bool "WiFi" - select WIFI_ENABLED if ESP32_ENABLE_STACK_WIFI -config ESP32_ENABLE_STACK_BT - bool "BT" - select MEMMAP_BT if ESP32_ENABLE_STACK_BT - select BT_ENABLED if ESP32_ENABLE_STACK_BT -#config ESP32_ENABLE_STACK_NONE -# bool "None" -#endchoice - -config SW_COEXIST_ENABLE - bool "Software do control of wifi/bt coexisit" - depends on ESP32_ENABLE_STACK_BT && ESP32_ENABLE_STACK_WIFI - default "n" - help - Software do something control of wifi/bt coexist. For some heavy traffic senario, - do sotware coexist, may be better. - -config MEMMAP_BT - bool - depends on ESP32_ENABLE_STACK_BT - help - The Bluetooth stack uses memory that cannot be used as generic memory anymore. This - reserves the space for that within the memory map of the compiled binary. - This option is required to enable BT stack. - Temporarily, this option is not compatible with WiFi stack. - config MEMMAP_SMP bool "Reserve memory for two cores" default "y" @@ -89,25 +54,57 @@ config TRACEMEM_RESERVE_DRAM default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS default 0x0 +choice ESP32_COREDUMP_TO_FLASH_OR_UART + prompt "Core dump destination" + default ESP32_ENABLE_COREDUMP_TO_NONE + help + Select place to store core dump: flash, uart or none (to disable core dumps generation). + + If core dump is configured to be stored in flash and custom partition table is used add + corresponding entry to your CSV. For examples, please see predefined partition table CSV descriptions + in the components/partition_table directory. + +config ESP32_ENABLE_COREDUMP_TO_FLASH + bool "Flash" + select ESP32_ENABLE_COREDUMP +config ESP32_ENABLE_COREDUMP_TO_UART + bool "UART" + select ESP32_ENABLE_COREDUMP +config ESP32_ENABLE_COREDUMP_TO_NONE + bool "None" +endchoice + +config ESP32_ENABLE_COREDUMP + bool + default F + help + Enables/disable core dump module. + +config ESP32_CORE_DUMP_UART_DELAY + int "Core dump print to UART delay" + depends on ESP32_ENABLE_COREDUMP_TO_UART + default 0 + help + Config delay (in ms) before printing core dump to UART. + Delay can be interrupted by pressing Enter key. + +config ESP32_CORE_DUMP_LOG_LEVEL + int "Core dump module logging level" + depends on ESP32_ENABLE_COREDUMP + default 1 + help + Config core dump module logging level (0-5). + # Not implemented and/or needs new silicon rev to work config MEMMAP_SPISRAM bool "Use external SPI SRAM chip as main memory" depends on ESP32_NEEDS_NEW_SILICON_REV default "n" help - The ESP32 can control an external SPI SRAM chip, adding the memory it contains to the + The ESP32 can control an external SPI SRAM chip, adding the memory it contains to the main memory map. Enable this if you have this hardware and want to use it in the same way as on-chip RAM. -config WIFI_ENABLED - bool - default "y" - depends on ESP32_ENABLE_STACK_WIFI - help - This compiles in the low-level WiFi stack. - - Temporarily, this option is not compatible with BT stack. - config SYSTEM_EVENT_QUEUE_SIZE int "System event queue size" default 32 @@ -137,6 +134,11 @@ config NEWLIB_STDOUT_ADDCR cursor one line down, not also move it to the beginning of the line. This is usually done by an added CR character. Enabling this will make the standard output code automatically add a CR character before a LF. + + With this option enabled, C standard library functions which read from UART + (like scanf) will convert "\r\n" character sequences back to "\n". + + This option doesn't affect behavior of the UART driver (drivers/uart.h). config NEWLIB_NANO_FORMAT bool "Enable 'nano' formatting options for printf/scanf family" @@ -145,7 +147,7 @@ config NEWLIB_NANO_FORMAT ESP32 ROM contains parts of newlib C library, including printf/scanf family of functions. These functions have been compiled with so-called "nano" formatting option. This option doesn't support 64-bit integer formats and C99 - features, such as positional arguments. + features, such as positional arguments. For more details about "nano" formatting option, please see newlib readme file, search for '--enable-newlib-nano-formatted-io': @@ -180,42 +182,42 @@ config CONSOLE_UART_NONE endchoice choice CONSOLE_UART_NUM - prompt "UART peripheral to use for console output (0-1)" - depends on CONSOLE_UART_CUSTOM + prompt "UART peripheral to use for console output (0-1)" + depends on CONSOLE_UART_CUSTOM default CONSOLE_UART_CUSTOM_NUM_0 help - Due of a ROM bug, UART2 is not supported for console output - via ets_printf. + Due of a ROM bug, UART2 is not supported for console output + via ets_printf. config CONSOLE_UART_CUSTOM_NUM_0 - bool "UART0" + bool "UART0" config CONSOLE_UART_CUSTOM_NUM_1 - bool "UART1" + bool "UART1" endchoice config CONSOLE_UART_NUM - int - default 0 if CONSOLE_UART_DEFAULT || CONSOLE_UART_NONE - default 0 if CONSOLE_UART_CUSTOM_NUM_0 - default 1 if CONSOLE_UART_CUSTOM_NUM_1 + int + default 0 if CONSOLE_UART_DEFAULT || CONSOLE_UART_NONE + default 0 if CONSOLE_UART_CUSTOM_NUM_0 + default 1 if CONSOLE_UART_CUSTOM_NUM_1 config CONSOLE_UART_TX_GPIO - int "UART TX on GPIO#" - depends on CONSOLE_UART_CUSTOM - range 0 33 - default 19 + int "UART TX on GPIO#" + depends on CONSOLE_UART_CUSTOM + range 0 33 + default 19 config CONSOLE_UART_RX_GPIO - int "UART RX on GPIO#" - depends on CONSOLE_UART_CUSTOM - range 0 39 - default 21 + int "UART RX on GPIO#" + depends on CONSOLE_UART_CUSTOM + range 0 39 + default 21 config CONSOLE_UART_BAUDRATE - int "UART console baud rate" - depends on !CONSOLE_UART_NONE - default 115200 - range 1200 4000000 + int "UART console baud rate" + depends on !CONSOLE_UART_NONE + default 115200 + range 1200 4000000 config ULP_COPROC_ENABLED bool "Enable Ultra Low Power (ULP) Coprocessor" @@ -246,13 +248,13 @@ choice ESP32_PANIC prompt "Panic handler behaviour" default ESP32_PANIC_PRINT_REBOOT help - If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is + If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is invoked. Configure the panic handlers action here. config ESP32_PANIC_PRINT_HALT bool "Print registers and halt" help - Outputs the relevant registers over the serial port and halt the + Outputs the relevant registers over the serial port and halt the processor. Needs a manual reset to restart. config ESP32_PANIC_PRINT_REBOOT @@ -335,8 +337,8 @@ config TASK_WDT_CHECK_IDLE_TASK help With this turned on, the task WDT can detect if the idle task is not called within the task watchdog timeout period. The idle task not being called usually is a symptom of another - task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the - idle task getting some runtime every now and then. Take Care: With this disabled, this + task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the + idle task getting some runtime every now and then. Take Care: With this disabled, this watchdog will trigger if no tasks register themselves within the timeout value. config TASK_WDT_CHECK_IDLE_TASK_CPU1 @@ -353,7 +355,7 @@ config BROWNOUT_DET default y depends on NEEDS_ESP32_NEW_SILICON_REV help - The ESP32 has a built-in brownout detector which can detect if the voltage is lower than + The ESP32 has a built-in brownout detector which can detect if the voltage is lower than a specific value. If this happens, it will reset the chip in order to prevent unintended behaviour. @@ -407,25 +409,27 @@ config BROWNOUT_DET_RESETDELAY choice ESP32_TIME_SYSCALL - prompt "Timers used for gettimeofday function" - default ESP32_TIME_SYSCALL_USE_RTC_FRC1 - help - This setting defines which hardware timers are used to - implement 'gettimeofday' and 'time' functions in C library. - - - If only FRC1 timer is used, gettimeofday will provide time at - microsecond resolution. Time will not be preserved when going - into deep sleep mode. - - If both FRC1 and RTC timers are used, timekeeping will - continue in deep sleep. Time will be reported at 1 microsecond - resolution. - - If only RTC timer is used, timekeeping will continue in - deep sleep, but time will be measured at 6.(6) microsecond - resolution. Also the gettimeofday function itself may take - longer to run. - - If no timers are used, gettimeofday and time functions - return -1 and set errno to ENOSYS. - + prompt "Timers used for gettimeofday function" + default ESP32_TIME_SYSCALL_USE_RTC_FRC1 + help + This setting defines which hardware timers are used to + implement 'gettimeofday' and 'time' functions in C library. + + - If only FRC1 timer is used, gettimeofday will provide time at + microsecond resolution. Time will not be preserved when going + into deep sleep mode. + - If both FRC1 and RTC timers are used, timekeeping will + continue in deep sleep. Time will be reported at 1 microsecond + resolution. + - If only RTC timer is used, timekeeping will continue in + deep sleep, but time will be measured at 6.(6) microsecond + resolution. Also the gettimeofday function itself may take + longer to run. + - If no timers are used, gettimeofday and time functions + return -1 and set errno to ENOSYS. + - When RTC is used for timekeeping, two RTC_STORE registers are + used to keep time in deep sleep mode. + config ESP32_TIME_SYSCALL_USE_RTC bool "RTC" config ESP32_TIME_SYSCALL_USE_RTC_FRC1 @@ -437,76 +441,102 @@ config ESP32_TIME_SYSCALL_USE_NONE endchoice choice ESP32_RTC_CLOCK_SOURCE - prompt "RTC clock source" - default ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC - help - Choose which clock is used as RTC clock source. - The only available option for now is to use internal - 150kHz RC oscillator. + prompt "RTC clock source" + default ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC + help + Choose which clock is used as RTC clock source. + The only available option for now is to use internal + 150kHz RC oscillator. config ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC - bool "Internal RC" + bool "Internal RC" config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL - bool "External 32kHz crystal" - depends on DOCUMENTATION_FOR_RTC_CNTL + bool "External 32kHz crystal" + depends on DOCUMENTATION_FOR_RTC_CNTL endchoice - config ESP32_DEEP_SLEEP_WAKEUP_DELAY - int "Extra delay in deep sleep wake stub (in us)" - default 0 - range 0 5000 - help - When ESP32 exits deep sleep, the CPU and the flash chip are powered on - at the same time. CPU will run deep sleep stub first, and then - proceed to load code from flash. Some flash chips need sufficient - time to pass between power on and first read operation. By default, - without any extra delay, this time is approximately 900us. - - If you are using a flash chip which needs more than 900us to become - ready after power on, set this parameter to add extra delay - to the default deep sleep stub. - - If you are seeing "flash read err, 1000" message printed to the - console after deep sleep reset, try increasing this value. + int "Extra delay in deep sleep wake stub (in us)" + default 0 + range 0 5000 + help + When ESP32 exits deep sleep, the CPU and the flash chip are powered on + at the same time. CPU will run deep sleep stub first, and then + proceed to load code from flash. Some flash chips need sufficient + time to pass between power on and first read operation. By default, + without any extra delay, this time is approximately 900us. + If you are using a flash chip which needs more than 900us to become + ready after power on, set this parameter to add extra delay + to the default deep sleep stub. -config ESP32_PHY_AUTO_INIT - bool "Initialize PHY in startup code" - default y - help - If enabled, PHY will be initialized in startup code, before - app_main function runs. - If this is undesired, disable this option and call esp_phy_init - from the application before enabling WiFi or BT. - - If this option is enabled, startup code will also initialize - NVS prior to initializing PHY. - - If unsure, choose 'y'. - -config ESP32_PHY_INIT_DATA_IN_PARTITION - bool "Use a partition to store PHY init data" - default n - help - If enabled, PHY init data will be loaded from a partition. - When using a custom partition table, make sure that PHY data - partition is included (type: 'data', subtype: 'phy'). - With default partition tables, this is done automatically. - If PHY init data is stored in a partition, it has to be flashed there, - otherwise runtime error will occur. - - If this option is not enabled, PHY init data will be embedded - into the application binary. - - If unsure, choose 'n'. - -config ESP32_PHY_MAX_TX_POWER - int "Max TX power (dBm)" - range 0 20 - default 20 - help - Set maximum transmit power. Actual transmit power for high - data rates may be lower than this setting. + If you are seeing "flash read err, 1000" message printed to the + console after deep sleep reset, try increasing this value. endmenu + +menuconfig WIFI_ENABLED + bool "WiFi" + default y + help + Select this option to enable WiFi stack and show the submenu with WiFi configuration choices. + +config SW_COEXIST_ENABLE + bool "Software controls WiFi/Bluetooth coexistence" + depends on WIFI_ENABLED && BT_ENABLED + default n + help + If enabled, WiFi & Bluetooth coexistence is controlled by software rather than hardware. + Recommended for heavy traffic scenarios. Both coexistence configuration options are + automatically managed, no user intervention is required. + +config ESP32_PHY_AUTO_INIT + bool "Initialize PHY in startup code" + depends on WIFI_ENABLED + default y + help + If enabled, PHY will be initialized in startup code, before + app_main function runs. + If this is undesired, disable this option and call esp_phy_init + from the application before enabling WiFi or BT. + + If this option is enabled, startup code will also initialize + NVS prior to initializing PHY. + + If unsure, choose 'y'. + +config ESP32_PHY_INIT_DATA_IN_PARTITION + bool "Use a partition to store PHY init data" + depends on WIFI_ENABLED + default n + help + If enabled, PHY init data will be loaded from a partition. + When using a custom partition table, make sure that PHY data + partition is included (type: 'data', subtype: 'phy'). + With default partition tables, this is done automatically. + If PHY init data is stored in a partition, it has to be flashed there, + otherwise runtime error will occur. + + If this option is not enabled, PHY init data will be embedded + into the application binary. + + If unsure, choose 'n'. + +config ESP32_PHY_MAX_TX_POWER + int "Max TX power (dBm)" + range 0 20 + default 20 + depends on WIFI_ENABLED + help + Set maximum transmit power. Actual transmit power for high + data rates may be lower than this setting. + +config ESP32_WIFI_RX_BUFFER_NUM + int "Max number of WiFi RX buffers" + depends on WIFI_ENABLED + range 2 25 + default 25 + help + Set the number of WiFi rx buffers. Each buffer takes approximately 1.6KB of RAM. + Larger number for higher throughput but more memory. Smaller number for lower + throughput but less memory. diff --git a/components/esp32/abi.cpp b/components/esp32/abi.cpp deleted file mode 100644 index c40dba85e0..0000000000 --- a/components/esp32/abi.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2015-2016 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. -#include -#include -#include -#include - -// using __cxxabiv1::__guard; - -void *operator new(size_t size) -{ - return malloc(size); -} - -void *operator new[](size_t size) -{ - return malloc(size); -} - -void operator delete(void * ptr) -{ - free(ptr); -} - -void operator delete[](void * ptr) -{ - free(ptr); -} - -extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__)); -extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); - -void __cxa_pure_virtual(void) -{ - abort(); -} - -void __cxa_deleted_virtual(void) -{ - abort(); -} - -#if 0 -typedef struct { - uint8_t guard; - uint8_t ps; -} guard_t; - -extern "C" int __cxa_guard_acquire(__guard* pg) -{ - uint8_t ps = xt_rsil(15); - if (reinterpret_cast(pg)->guard) { - xt_wsr_ps(ps); - return 0; - } - reinterpret_cast(pg)->ps = ps; - return 1; -} - -extern "C" void __cxa_guard_release(__guard* pg) -{ - reinterpret_cast(pg)->guard = 1; - xt_wsr_ps(reinterpret_cast(pg)->ps); -} - -extern "C" void __cxa_guard_abort(__guard* pg) -{ - xt_wsr_ps(reinterpret_cast(pg)->ps); -} -#endif - -extern "C" void __cxa_throw_bad_array_new_length() -{ - abort(); -} - -namespace std -{ -void __throw_bad_function_call() -{ - abort(); -} - -void __throw_length_error(char const*) -{ - abort(); -} - -void __throw_bad_alloc() -{ - abort(); -} - -void __throw_logic_error(const char* str) -{ - abort(); -} - -void __throw_out_of_range(const char* str) -{ - abort(); -} -} diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 4ceb13e837..e1cd2c6524 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -3,8 +3,15 @@ # COMPONENT_SRCDIRS := . hwcrypto +LIBS := core rtc phy +ifdef CONFIG_BT_ENABLED +LIBS += coexist +endif +ifdef CONFIG_WIFI_ENABLED +LIBS += net80211 pp wpa smartconfig coexist wps wpa2 +endif -LIBS := core net80211 phy rtc pp wpa smartconfig coexist wps wpa2 +LIBS := $(sort $(LIBS)) # de-duplicate, we can handle different orders here LINKER_SCRIPTS += esp32.common.ld esp32.rom.ld esp32.peripherals.ld diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c new file mode 100644 index 0000000000..85301f4dd6 --- /dev/null +++ b/components/esp32/core_dump.c @@ -0,0 +1,474 @@ +// Copyright 2015-2016 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. +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "soc/uart_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/gpio.h" + +#include "esp_panic.h" +#include "esp_partition.h" + +#if CONFIG_ESP32_ENABLE_COREDUMP +#define LOG_LOCAL_LEVEL CONFIG_ESP32_CORE_DUMP_LOG_LEVEL +#include "esp_log.h" +const static char *TAG = "esp_core_dump"; + +#define ESP_COREDUMP_LOGE( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) { ets_printf(LOG_FORMAT(E, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGW( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) { ets_printf(LOG_FORMAT(W, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGI( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) { ets_printf(LOG_FORMAT(I, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGD( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { ets_printf(LOG_FORMAT(D, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGV( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) { ets_printf(LOG_FORMAT(V, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +#define ESP_COREDUMP_LOG_PROCESS( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { ets_printf(LOG_FORMAT(D, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#else +#define ESP_COREDUMP_LOG_PROCESS( format, ... ) do{/*(__VA_ARGS__);*/}while(0) +#endif + + +// TODO: allow user to set this in menuconfig or get tasks iteratively +#define COREDUMP_MAX_TASKS_NUM 32 + +typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len); +typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv); +typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv); +typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len); + +typedef struct _core_dump_write_config_t +{ + esp_core_dump_write_prepare_t prepare; + esp_core_dump_write_start_t start; + esp_core_dump_write_end_t end; + esp_core_dump_flash_write_data_t write; + void * priv; + +} core_dump_write_config_t; + +static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg) +{ + union + { + uint8_t data8[12]; + uint32_t data32[3]; + } rom_data; + esp_err_t err; + TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM]; + UBaseType_t tcb_sz, task_num; + uint32_t data_len = 0, i, len; + + task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz); + // take TCB padding into account, actual TCB size will be stored in header + if (tcb_sz % sizeof(uint32_t)) + len = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); + else + len = tcb_sz; + // header + tasknum*(tcb + stack start/end + tcb addr) + data_len = 3*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *)); + for (i = 0; i < task_num; i++) { + if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) { + // set correct stack top for current task + tasks[i].pxTopOfStack = (StackType_t *)frame; + ESP_COREDUMP_LOG_PROCESS("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x", frame->exit, frame->pc, frame->ps, frame->a0, frame->a1); + } + else { + XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack; + if (task_frame->exit == 0) { + ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1); + } + else { +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack; +#endif + ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1); + } + } +#if( portSTACK_GROWTH < 0 ) + len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack; +#else + len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; +#endif + ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack); + // take stack padding into account + if (len % sizeof(uint32_t)) + len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t); + data_len += len; + } + + // prepare write + if (write_cfg->prepare) { + err = write_cfg->prepare(write_cfg->priv, &data_len); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to prepare core dump (%d)!", err); + return; + } + } + + ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu", data_len); + + // write start + if (write_cfg->start) { + err = write_cfg->start(write_cfg->priv); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err); + return; + } + } + + // write header + rom_data.data32[0] = data_len; + rom_data.data32[1] = task_num; + rom_data.data32[2] = tcb_sz; + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err); + return; + } + + // write tasks + for (i = 0; i < task_num; i++) { + ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].pxTCB); + // save TCB address, stack base and stack top addr + rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; + rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; + rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err); + return; + } + // save TCB + err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err); + return; + } + // save task stack + err = write_cfg->write(write_cfg->priv, +#if( portSTACK_GROWTH < 0 ) + tasks[i].pxTopOfStack, + (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack +#else + tasks[i].pxEndOfStack, + (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack +#endif + ); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err); + return; + } + } + + // write end + if (write_cfg->end) { + err = write_cfg->end(write_cfg->priv); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err); + return; + } + } +} + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + +// magic numbers to control core dump data consistency +#define COREDUMP_FLASH_MAGIC_START 0xE32C04EDUL +#define COREDUMP_FLASH_MAGIC_END 0xE32C04EDUL + +typedef struct _core_dump_write_flash_data_t +{ + uint32_t off; +} core_dump_write_flash_data_t; + +// core dump partition start +static uint32_t s_core_part_start; +// core dump partition size +static uint32_t s_core_part_size; + +static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint32_t data_size) +{ + esp_err_t err; + uint32_t data_len = 0, k, len; + union + { + uint8_t data8[4]; + uint32_t data32; + } rom_data; + + data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t); + err = spi_flash_write(off, data, data_len); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err); + return 0; + } + + len = data_size % sizeof(uint32_t); + if (len) { + // write last bytes with padding, actual TCB len can be retrieved by esptool from core dump header + rom_data.data32 = 0; + for (k = 0; k < len; k++) + rom_data.data8[k] = *(data + data_len + k); + err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err); + return 0; + } + data_len += sizeof(uint32_t); + } + + return data_len; +} + +static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) +{ + esp_err_t err; + uint32_t sec_num; + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + + // add space for 2 magics. TODO: change to CRC + if ((*data_len + 2*sizeof(uint32_t)) > s_core_part_size) { + ESP_COREDUMP_LOGE("Not enough space to save core dump!"); + return ESP_ERR_NO_MEM; + } + *data_len += 2*sizeof(uint32_t); + + wr_data->off = 0; + + sec_num = *data_len / SPI_FLASH_SEC_SIZE; + if (*data_len % SPI_FLASH_SEC_SIZE) + sec_num++; + err = spi_flash_erase_range(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err); + return err; + } + + return err; +} + +static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr_data, uint32_t word) +{ + esp_err_t err = ESP_OK; + uint32_t data32 = word; + + err = spi_flash_write(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write to flash (%d)!", err); + return err; + } + wr_data->off += sizeof(uint32_t); + + return err; +} + +static esp_err_t esp_core_dump_flash_write_start(void *priv) +{ + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + // save magic 1 + return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_START); +} + +static esp_err_t esp_core_dump_flash_write_end(void *priv) +{ + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; +#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG + uint32_t i; + union + { + uint8_t data8[16]; + uint32_t data32[4]; + } rom_data; + + esp_err_t err = spi_flash_read(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to read flash (%d)!", err); + return err; + } + else { + ESP_COREDUMP_LOG_PROCESS("Data from flash:"); + for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]); + } + } +#endif + + // save magic 2 + return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_END); +} + +static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) +{ + esp_err_t err = ESP_OK; + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + + uint32_t len = esp_core_dump_write_flash_padded(s_core_part_start + wr_data->off, data, data_len); + if (len != data_len) + return ESP_FAIL; + + wr_data->off += len; + + return err; +} + +void esp_core_dump_to_flash(XtExcFrame *frame) +{ + core_dump_write_config_t wr_cfg; + core_dump_write_flash_data_t wr_data; + + /* init non-OS flash access critical section */ + spi_flash_guard_set(&g_flash_guard_no_os_ops); + + wr_cfg.prepare = esp_core_dump_flash_write_prepare; + wr_cfg.start = esp_core_dump_flash_write_start; + wr_cfg.end = esp_core_dump_flash_write_end; + wr_cfg.write = esp_core_dump_flash_write_data; + wr_cfg.priv = &wr_data; + + ESP_COREDUMP_LOGI("Save core dump to flash..."); + esp_core_dump_write(frame, &wr_cfg); + ESP_COREDUMP_LOGI("Core dump has been saved to flash."); +} +#endif + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { + static const char *b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; + + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; + + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 0x0F) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 0x3F]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; +} + +static esp_err_t esp_core_dump_uart_write_start(void *priv) +{ + esp_err_t err = ESP_OK; + ets_printf("================= CORE DUMP START =================\r\n"); + return err; +} + +static esp_err_t esp_core_dump_uart_write_end(void *priv) +{ + esp_err_t err = ESP_OK; + ets_printf("================= CORE DUMP END =================\r\n"); + return err; +} + +static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) +{ + esp_err_t err = ESP_OK; + char buf[64 + 4], *addr = data; + char *end = addr + data_len; + + while (addr < end) { + size_t len = end - addr; + if (len > 48) len = 48; + /* Copy to stack to avoid alignment restrictions. */ + char *tmp = buf + (sizeof(buf) - len); + memcpy(tmp, addr, len); + esp_core_dump_b64_encode((const uint8_t *)tmp, len, (uint8_t *)buf); + addr += len; + ets_printf("%s\r\n", buf); + } + + return err; +} + +static int esp_core_dump_uart_get_char() { + int i; + uint32_t reg = (READ_PERI_REG(UART_STATUS_REG(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT; + if (reg) + i = READ_PERI_REG(UART_FIFO_REG(0)); + else + i = -1; + return i; +} + +void esp_core_dump_to_uart(XtExcFrame *frame) +{ + core_dump_write_config_t wr_cfg; + uint32_t tm_end, tm_cur; + int ch; + + wr_cfg.prepare = NULL; + wr_cfg.start = esp_core_dump_uart_write_start; + wr_cfg.end = esp_core_dump_uart_write_end; + wr_cfg.write = esp_core_dump_uart_write_data; + wr_cfg.priv = NULL; + + //Make sure txd/rxd are enabled + gpio_pullup_dis(1); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); + + ESP_COREDUMP_LOGI("Press Enter to print core dump to UART..."); + tm_end = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000) + CONFIG_ESP32_CORE_DUMP_UART_DELAY; + ch = esp_core_dump_uart_get_char(); + while (!(ch == '\n' || ch == '\r')) { + tm_cur = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000); + if (tm_cur >= tm_end) + break; + /* Feed the Cerberus. */ + TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_feed = 1; + TIMERG0.wdt_wprotect = 0; + ch = esp_core_dump_uart_get_char(); + } + ESP_COREDUMP_LOGI("Print core dump to uart..."); + esp_core_dump_write(frame, &wr_cfg); + ESP_COREDUMP_LOGI("Core dump has been written to uart."); +} +#endif + +void esp_core_dump_init() +{ +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + const esp_partition_t *core_part; + + ESP_LOGI(TAG, "Init core dump to flash"); + core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL); + if (!core_part) { + ESP_LOGE(TAG, "No core dump partition found!"); + return; + } + ESP_LOGI(TAG, "Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size); + s_core_part_start = core_part->address; + s_core_part_size = core_part->size; +#endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART + ESP_LOGI(TAG, "Init core dump to UART"); +#endif +} + +#endif + diff --git a/components/esp32/cpu_freq.c b/components/esp32/cpu_freq.c index 257639cf90..7618f147af 100644 --- a/components/esp32/cpu_freq.c +++ b/components/esp32/cpu_freq.c @@ -13,6 +13,7 @@ // limitations under the License. #include +#include "esp_attr.h" #include "rom/ets_sys.h" #include "rom/uart.h" #include "sdkconfig.h" @@ -66,3 +67,10 @@ void esp_set_cpu_freq(void) ets_update_cpu_frequency(freq_mhz); } +void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) +{ + extern uint32_t g_ticks_per_us_pro; // g_ticks_us defined in ROM for PRO CPU + extern uint32_t g_ticks_per_us_app; // same defined for APP CPU + g_ticks_per_us_pro = ticks_per_us; + g_ticks_per_us_app = ticks_per_us; +} diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 864a17a1b6..b5e896e573 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -55,6 +55,7 @@ #include "esp_task_wdt.h" #include "esp_phy_init.h" #include "esp_coexist.h" +#include "esp_core_dump.h" #include "trax.h" #define STRINGIFY(s) STRINGIFY2(s) @@ -70,9 +71,11 @@ static bool app_cpu_started = false; #endif //!CONFIG_FREERTOS_UNICORE static void do_global_ctors(void); -static void do_phy_init(); static void main_task(void* args); extern void app_main(void); +#if CONFIG_ESP32_PHY_AUTO_INIT +static void do_phy_init(); +#endif extern int _bss_start; extern int _bss_end; @@ -202,6 +205,8 @@ void start_cpu0_default(void) #endif esp_ipc_init(); spi_flash_init(); + /* init default OS-aware flash access critical section */ + spi_flash_guard_set(&g_flash_guard_default_ops); #if CONFIG_ESP32_PHY_AUTO_INIT nvs_flash_init(); @@ -214,6 +219,10 @@ void start_cpu0_default(void) } #endif +#if CONFIG_ESP32_ENABLE_COREDUMP + esp_core_dump_init(); +#endif + xTaskCreatePinnedToCore(&main_task, "main", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL, 0); @@ -257,6 +266,7 @@ static void main_task(void* args) vTaskDelete(NULL); } +#if CONFIG_ESP32_PHY_AUTO_INIT static void do_phy_init() { esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL; @@ -290,3 +300,5 @@ static void do_phy_init() esp_phy_release_init_data(init_data); free(cal_data); // PHY maintains a copy of calibration data, so we can free this } +#endif //CONFIG_ESP32_PHY_AUTO_INIT + diff --git a/components/esp32/cpu_util.c b/components/esp32/cpu_util.c index cff61ab796..ecfcab4baf 100644 --- a/components/esp32/cpu_util.c +++ b/components/esp32/cpu_util.c @@ -16,6 +16,7 @@ #include "soc/cpu.h" #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" +#include "sdkconfig.h" void IRAM_ATTR esp_cpu_stall(int cpu_id) { @@ -42,3 +43,15 @@ void IRAM_ATTR esp_cpu_unstall(int cpu_id) CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_PROCPU_C0_M); } } + +bool IRAM_ATTR esp_cpu_in_ocd_debug_mode() +{ +#if CONFIG_ESP32_DEBUG_OCDAWARE + int dcr; + int reg=0x10200C; //DSRSET register + asm("rer %0,%1":"=r"(dcr):"r"(reg)); + return (dcr&0x1); +#else + return false; // Always return false if "OCD aware" is disabled +#endif +} diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index f7ea4f6a74..1e131eeef7 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -44,7 +44,7 @@ static volatile uint32_t reason[ portNUM_PROCESSORS ]; ToDo: There is a small chance the CPU already has yielded when this ISR is serviced. In that case, it's running the intended task but the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. */ -static void esp_crosscore_isr(void *arg) { +static void IRAM_ATTR esp_crosscore_isr(void *arg) { uint32_t myReasonVal; //A pointer to the correct reason array item is passed to this ISR. volatile uint32_t *myReason=arg; @@ -73,11 +73,13 @@ void esp_crosscore_int_init() { portENTER_CRITICAL(&reasonSpinlock); reason[xPortGetCoreID()]=0; portEXIT_CRITICAL(&reasonSpinlock); + esp_err_t err; if (xPortGetCoreID()==0) { - esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); + err = esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } else { - esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); + err = esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } + assert(err == ESP_OK); } void esp_crosscore_int_send_yield(int coreId) { diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 7f59c21635..3eef7ca29d 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -91,7 +91,7 @@ void RTC_IRAM_ATTR esp_default_wake_deep_sleep(void) { #if CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY > 0 // ROM code has not started yet, so we need to set delay factor // used by ets_delay_us first. - ets_update_cpu_frequency(ets_get_detected_xtal_freq() / 1000000); + ets_update_cpu_frequency_rom(ets_get_detected_xtal_freq() / 1000000); // This delay is configured in menuconfig, it can be used to give // the flash chip some time to become ready. ets_delay_us(CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY); @@ -302,12 +302,18 @@ static uint32_t get_power_down_flags() { // Where needed, convert AUTO options to ON. Later interpret AUTO as OFF. - // RTC_SLOW_MEM is needed only for the ULP. - // If RTC_SLOW_MEM is Auto, and ULP wakeup isn't enabled, power down RTC_SLOW_MEM. - if (s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] == ESP_PD_OPTION_AUTO) { - if (s_config.wakeup_triggers & RTC_SAR_TRIG_EN) { - s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] = ESP_PD_OPTION_ON; - } + // RTC_SLOW_MEM is needed for the ULP, so keep RTC_SLOW_MEM powered up if ULP + // is used and RTC_SLOW_MEM is Auto. + // If there is any data placed into .rtc.data or .rtc.bss segments, and + // RTC_SLOW_MEM is Auto, keep it powered up as well. + + // These labels are defined in the linker script: + extern int _rtc_data_start, _rtc_data_end, _rtc_bss_start, _rtc_bss_end; + + if (s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] == ESP_PD_OPTION_AUTO || + &_rtc_data_end > &_rtc_data_start || + &_rtc_bss_end > &_rtc_bss_start) { + s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] = ESP_PD_OPTION_ON; } // RTC_FAST_MEM is needed for deep sleep stub. diff --git a/components/esp32/heap_alloc_caps.c b/components/esp32/heap_alloc_caps.c index 7bb3b40879..a4ff870f39 100644 --- a/components/esp32/heap_alloc_caps.c +++ b/components/esp32/heap_alloc_caps.c @@ -170,7 +170,7 @@ Warning: These variables are assumed to have the start and end of the data and i area used statically by the program, respectively. These variables are defined in the ld file. */ -extern int _bss_start, _heap_start, _init_start, _iram_text_end; +extern int _data_start, _heap_start, _init_start, _iram_text_end; /* Initialize the heap allocator. We pass it a bunch of region descriptors, but we need to modify those first to accommodate for @@ -183,7 +183,7 @@ void heap_alloc_caps_init() { //Compile-time assert to see if we don't have more tags than is set in heap_regions.h _Static_assert((sizeof(tag_desc)/sizeof(tag_desc[0]))-1 <= HEAPREGIONS_MAX_TAGCOUNT, "More than HEAPREGIONS_MAX_TAGCOUNT tags defined!"); //Disable the bits of memory where this code is loaded. - disable_mem_region(&_bss_start, &_heap_start); //DRAM used by bss/data static variables + disable_mem_region(&_data_start, &_heap_start); //DRAM used by bss/data static variables disable_mem_region(&_init_start, &_iram_text_end); //IRAM used by code disable_mem_region((void*)0x3ffae000, (void*)0x3ffb0000); //knock out ROM data region disable_mem_region((void*)0x40070000, (void*)0x40078000); //CPU0 cache region @@ -192,7 +192,7 @@ void heap_alloc_caps_init() { // TODO: this region should be checked, since we don't need to knock out all region finally disable_mem_region((void*)0x3ffe0000, (void*)0x3ffe8000); //knock out ROM data region -#if CONFIG_MEMMAP_BT +#if CONFIG_BT_ENABLED disable_mem_region((void*)0x3ffb0000, (void*)0x3ffc0000); //knock out BT data region #endif diff --git a/components/esp32/hw_random.c b/components/esp32/hw_random.c index 11c7af936b..3e5cb81a0a 100644 --- a/components/esp32/hw_random.c +++ b/components/esp32/hw_random.c @@ -29,13 +29,19 @@ uint32_t IRAM_ATTR esp_random(void) * this function needs to wait for at least 16 APB clock cycles after reading * previous word. This implementation may actually wait a bit longer * due to extra time spent in arithmetic and branch statements. + * + * As a (probably unncessary) precaution to avoid returning the + * RNG state as-is, the result is XORed with additional + * WDEV_RND_REG reads while waiting. */ static uint32_t last_ccount = 0; uint32_t ccount; + uint32_t result = 0; do { ccount = XTHAL_GET_CCOUNT(); + result ^= REG_READ(WDEV_RND_REG); } while (ccount - last_ccount < XT_CLOCK_FREQ / APB_CLK_FREQ * 16); last_ccount = ccount; - return REG_READ(WDEV_RND_REG); + return result ^ REG_READ(WDEV_RND_REG); } diff --git a/components/esp32/include/esp_attr.h b/components/esp32/include/esp_attr.h index 7ef2920d98..911201aace 100644 --- a/components/esp32/include/esp_attr.h +++ b/components/esp32/include/esp_attr.h @@ -26,7 +26,7 @@ // Forces data into DRAM instead of flash #define DRAM_ATTR __attribute__((section(".dram1"))) -// Forces a string into DRAM instrad of flash +// Forces a string into DRAM instead of flash // Use as ets_printf(DRAM_STR("Hello world!\n")); #define DRAM_STR(str) (__extension__({static const DRAM_ATTR char __c[] = (str); (const char *)&__c;})) diff --git a/components/esp32/include/esp_core_dump.h b/components/esp32/include/esp_core_dump.h new file mode 100644 index 0000000000..c6634364c5 --- /dev/null +++ b/components/esp32/include/esp_core_dump.h @@ -0,0 +1,64 @@ +// Copyright 2015-2016 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 ESP_CORE_DUMP_H_ +#define ESP_CORE_DUMP_H_ + +/** + * @brief Initializes core dump module internal data. + * + * @note Should be called at system startup. + */ +void esp_core_dump_init(); + +/** + * @brief Saves core dump to flash. + * + * The structure of data stored in flash is as follows: + * | MAGIC1 | + * | TOTAL_LEN | TASKS_NUM | TCB_SIZE | + * | TCB_ADDR_1 | STACK_TOP_1 | STACK_END_1 | TCB_1 | STACK_1 | + * . . . . + * . . . . + * | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N | + * | MAGIC2 | + * Core dump in flash consists of header and data for every task in the system at the moment of crash. + * For flash data integrity control two magic numbers are used at the beginning and the end of core dump. + * The structure of core dump data is described below in details. + * 1) MAGIC1 and MAGIC2 are special numbers stored at the beginning and the end of core dump. + * They are used to control core dump data integrity. Size of every number is 4 bytes. + * 2) Core dump starts with header: + * 2.1) TOTAL_LEN is total length of core dump data in flash including magic numbers. Size is 4 bytes. + * 2.2) TASKS_NUM is the number of tasks for which data are stored. Size is 4 bytes. + * 2.3) TCB_SIZE is the size of task's TCB structure. Size is 4 bytes. + * 3) Core dump header is followed by the data for every task in the system. + * Task data are started with task header: + * 3.1) TCB_ADDR is the address of TCB in memory. Size is 4 bytes. + * 3.2) STACK_TOP is the top of task's stack (address of the topmost stack item). Size is 4 bytes. + * 3.2) STACK_END is the end of task's stack (address from which task's stack starts). Size is 4 bytes. + * 4) Task header is followed by TCB data. Size is TCB_SIZE bytes. + * 5) Task's stack is placed after TCB data. Size is (STACK_END - STACK_TOP) bytes. + */ +void esp_core_dump_to_flash(); + +/** + * @brief Print base64-encoded core dump to UART. + * + * The structure of core dump data is the same as for data stored in flash (@see esp_core_dump_to_flash) with some notes: + * 1) Magic numbers are not present in core dump printed to UART. + * 2) Since magic numbers are omitted TOTAL_LEN does not include their size. + * 3) Printed base64 data are surrounded with special messages to help user recognize the start and end of actual data. + */ +void esp_core_dump_to_uart(); + +#endif diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index a1f4b8f359..b6a1e8b421 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -35,7 +35,8 @@ typedef int32_t esp_err_t; #define ESP_ERR_NOT_FOUND 0x105 #define ESP_ERR_NOT_SUPPORTED 0x106 #define ESP_ERR_TIMEOUT 0x107 - +#define ESP_ERR_INVALID_RESPONSE 0x108 +#define ESP_ERR_INVALID_CRC 0x109 #define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */ diff --git a/components/esp32/include/esp_intr_alloc.h b/components/esp32/include/esp_intr_alloc.h index c1f91dd2e3..7195d07d87 100644 --- a/components/esp32/include/esp_intr_alloc.h +++ b/components/esp32/include/esp_intr_alloc.h @@ -124,6 +124,9 @@ esp_err_t esp_intr_reserve(int intno, int cpu); * * The interrupt will always be allocated on the core that runs this function. * + * If ESP_INTR_FLAG_IRAM flag is used, and handler address is not in IRAM or + * RTC_FAST_MEM, then ESP_ERR_INVALID_ARG is returned. + * * @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux * sources, as defined in soc/soc.h, or one of the internal * ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header. @@ -264,4 +267,4 @@ void esp_intr_noniram_enable(); } #endif -#endif \ No newline at end of file +#endif diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index 6aba6c5f48..aa83c6d381 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -14,8 +14,49 @@ #ifndef __ASSEMBLER__ +#include "esp_err.h" + + +/** + * @brief If an OCD is connected over JTAG. set breakpoint 0 to the given function + * address. Do nothing otherwise. + * @param data Pointer to the target breakpoint position + */ + void esp_set_breakpoint_if_jtag(void *fn); +#define ESP_WATCHPOINT_LOAD 0x40000000 +#define ESP_WATCHPOINT_STORE 0x80000000 +#define ESP_WATCHPOINT_ACCESS 0xC0000000 + +/** + * @brief Set a watchpoint to break/panic when a certain memory range is accessed. + * + * @param no Watchpoint number. On the ESP32, this can be 0 or 1. + * @param adr Base address to watch + * @param size Size of the region, starting at the base address, to watch. Must + * be one of 2^n, with n in [0..6]. + * @param flags One of ESP_WATCHPOINT_* flags + * + * @return ESP_ERR_INVALID_ARG on invalid arg, ESP_OK otherwise + * + * @warning The ESP32 watchpoint hardware watches a region of bytes by effectively + * masking away the lower n bits for a region with size 2^n. If adr does + * not have zero for these lower n bits, you may not be watching the + * region you intended. + */ +esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags); + + +/** + * @brief Clear a watchpoint + * + * @param no Watchpoint to clear + * + */ +void esp_clear_watchpoint(int no); + + #endif -#endif \ No newline at end of file +#endif diff --git a/components/esp32/include/esp_phy_init.h b/components/esp32/include/esp_phy_init.h index 7bc0536103..e669a44151 100644 --- a/components/esp32/include/esp_phy_init.h +++ b/components/esp32/include/esp_phy_init.h @@ -229,7 +229,7 @@ esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_da * * Applications which don't need to enable PHY on every start up should * disable this menuconfig option and call esp_phy_init before calling - * esp_wifi_init or bt_controller_init. See do_phy_init function in + * esp_wifi_init or esp_bt_controller_init. See do_phy_init function in * cpu_start.c for an example of using this function. * * @param init_data PHY parameters. Default set of parameters can diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 68d06aae52..8d4fa17bb7 100755 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -62,6 +62,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "rom/queue.h" +#include "sdkconfig.h" #include "esp_err.h" #include "esp_wifi_types.h" #include "esp_event.h" @@ -76,8 +77,8 @@ extern "C" { #define ESP_ERR_WIFI_ARG ESP_ERR_INVALID_ARG /*!< Invalid argument */ #define ESP_ERR_WIFI_NOT_SUPPORT ESP_ERR_NOT_SUPPORTED /*!< Indicates that API is not supported yet */ -#define ESP_ERR_WIFI_NOT_INIT (ESP_ERR_WIFI_BASE + 1) /*!< WiFi driver is not installed by esp_wifi_init */ -#define ESP_ERR_WIFI_NOT_START (ESP_ERR_WIFI_BASE + 2) /*!< WiFi driver is not started by esp_wifi_start */ +#define ESP_ERR_WIFI_NOT_INIT (ESP_ERR_WIFI_BASE + 1) /*!< WiFi driver was not installed by esp_wifi_init */ +#define ESP_ERR_WIFI_NOT_STARTED (ESP_ERR_WIFI_BASE + 2) /*!< WiFi driver was not started by esp_wifi_start */ #define ESP_ERR_WIFI_IF (ESP_ERR_WIFI_BASE + 3) /*!< WiFi interface error */ #define ESP_ERR_WIFI_MODE (ESP_ERR_WIFI_BASE + 4) /*!< WiFi mode error */ #define ESP_ERR_WIFI_STATE (ESP_ERR_WIFI_BASE + 5) /*!< WiFi internal state error */ @@ -85,7 +86,7 @@ extern "C" { #define ESP_ERR_WIFI_NVS (ESP_ERR_WIFI_BASE + 7) /*!< WiFi internal NVS module error */ #define ESP_ERR_WIFI_MAC (ESP_ERR_WIFI_BASE + 8) /*!< MAC address is invalid */ #define ESP_ERR_WIFI_SSID (ESP_ERR_WIFI_BASE + 9) /*!< SSID is invalid */ -#define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 10) /*!< Passord is invalid */ +#define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 10) /*!< Password is invalid */ #define ESP_ERR_WIFI_TIMEOUT (ESP_ERR_WIFI_BASE + 11) /*!< Timeout error */ #define ESP_ERR_WIFI_WAKE_FAIL (ESP_ERR_WIFI_BASE + 12) /*!< WiFi is in sleep state(RF closed) and wakeup fail */ @@ -94,12 +95,17 @@ extern "C" { */ typedef struct { system_event_handler_t event_handler; /**< WiFi event handler */ + uint32_t rx_buf_num; /**< WiFi RX buffer number */ } wifi_init_config_t; - +#ifdef CONFIG_WIFI_ENABLED #define WIFI_INIT_CONFIG_DEFAULT() { \ .event_handler = &esp_event_send, \ + .rx_buf_num = CONFIG_ESP32_WIFI_RX_BUFFER_NUM, \ }; +#else +#define WIFI_INIT_CONFIG_DEFAULT #error Wifi is disabled in config, WIFI_INIT_CONFIG_DEFAULT will not work +#endif /** * @brief Init WiFi @@ -107,17 +113,15 @@ typedef struct { * WiFi NVS structure etc, this WiFi also start WiFi task * * @attention 1. This API must be called before all other WiFi API can be called - * @attention 2. Generally we should init event_q in *config, WiFi driver will post the event - * to this queue when event happens, such as, when station connects to WiFi, WiFi driver - * will post station connected event to this queue. If the queue is not initialized, WiFi - * will not post any events + * @attention 2. event_handler field in cfg should be set to a valid event handler function. + * In most cases, use the WIFI_INIT_CONFIG_DEFAULT macro which sets esp_event_send(). * * @param config provide WiFi init configuration * * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NO_MEM: out of memory - * - others: refer to error code esp_err.h + * - others: refer to error code esp_err.h */ esp_err_t esp_wifi_init(wifi_init_config_t *config); @@ -222,8 +226,8 @@ esp_err_t esp_wifi_connect(void); * * @return * - ESP_OK: succeed - * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_INIT: WiFi was not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_STARTED: WiFi was not started by esp_wifi_start * - ESP_ERR_WIFI_FAIL: other WiFi internal errors */ esp_err_t esp_wifi_disconnect(void); @@ -246,7 +250,7 @@ esp_err_t esp_wifi_clear_fast_connect(void); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi was not started by esp_wifi_start * - ESP_ERR_WIFI_ARG: invalid argument * - ESP_ERR_WIFI_MODE: WiFi mode is wrong */ @@ -266,7 +270,7 @@ esp_err_t esp_wifi_deauth_sta(uint16_t aid); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi was not started by esp_wifi_start * - ESP_ERR_WIFI_TIMEOUT: blocking scan is timeout * - others: refer to error code in esp_err.h */ @@ -278,7 +282,7 @@ esp_err_t esp_wifi_scan_start(wifi_scan_config_t *config, bool block); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi is not started by esp_wifi_start */ esp_err_t esp_wifi_scan_stop(void); @@ -292,7 +296,7 @@ esp_err_t esp_wifi_scan_stop(void); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi is not started by esp_wifi_start * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); @@ -307,7 +311,7 @@ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi is not started by esp_wifi_start * - ESP_ERR_WIFI_ARG: invalid argument * - ESP_ERR_WIFI_NO_MEM: out of memory */ diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index 583d7a6a91..88ad3dcf5b 100755 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -96,7 +96,7 @@ typedef enum { } wifi_second_chan_t; typedef struct { - char *ssid; /**< SSID of AP */ + uint8_t *ssid; /**< SSID of AP */ uint8_t *bssid; /**< MAC address of AP */ uint8_t channel; /**< channel, scan the specific channel */ bool show_hidden; /**< enable to scan AP whose SSID is hidden */ @@ -109,6 +109,8 @@ typedef struct { wifi_second_chan_t second; /**< second channel of AP */ int8_t rssi; /**< signal strength of AP */ wifi_auth_mode_t authmode; /**< authmode of AP */ + uint32_t low_rate_enable:1; /**< bit: 0 flag to identify if low rate is enabled or not */ + uint32_t reserved:31; /**< bit: 1..31 reserved */ } wifi_ap_record_t; typedef enum { @@ -119,15 +121,16 @@ typedef enum { #define WIFI_PROTOCOL_11B 1 #define WIFI_PROTOCOL_11G 2 #define WIFI_PROTOCOL_11N 4 +#define WIFI_PROTOCOL_LR 8 typedef enum { - WIFI_BW_HT20 = 0, /* Bandwidth is HT20 */ + WIFI_BW_HT20 = 1, /* Bandwidth is HT20 */ WIFI_BW_HT40, /* Bandwidth is HT40 */ } wifi_bandwidth_t; typedef struct { - char ssid[32]; /**< SSID of ESP32 soft-AP */ - char password[64]; /**< Password of ESP32 soft-AP */ + uint8_t ssid[32]; /**< SSID of ESP32 soft-AP */ + uint8_t password[64]; /**< Password of ESP32 soft-AP */ uint8_t ssid_len; /**< Length of SSID. If softap_config.ssid_len==0, check the SSID until there is a termination character; otherwise, set the SSID length according to softap_config.ssid_len. */ uint8_t channel; /**< Channel of ESP32 soft-AP */ wifi_auth_mode_t authmode; /**< Auth mode of ESP32 soft-AP. Do not support AUTH_WEP in soft-AP mode */ @@ -137,8 +140,8 @@ typedef struct { } wifi_ap_config_t; typedef struct { - char ssid[32]; /**< SSID of target AP*/ - char password[64]; /**< password of target AP*/ + uint8_t ssid[32]; /**< SSID of target AP*/ + uint8_t password[64]; /**< password of target AP*/ bool bssid_set; /**< whether set MAC address of target AP or not. Generally, station_config.bssid_set needs to be 0; and it needs to be 1 only when users need to check the MAC address of the AP.*/ uint8_t bssid[6]; /**< MAC address of target AP*/ } wifi_sta_config_t; @@ -215,7 +218,7 @@ typedef struct { typedef struct { wifi_pkt_rx_ctrl_t rx_ctrl; - char payload[0]; /**< ieee80211 packet buff, The length of payload is described by sig_len */ + uint8_t payload[0]; /**< ieee80211 packet buff, The length of payload is described by sig_len */ } wifi_promiscuous_pkt_t; /** diff --git a/components/esp32/include/esp_wpa2.h b/components/esp32/include/esp_wpa2.h index 1857d19a16..e33e0e6dcf 100644 --- a/components/esp32/include/esp_wpa2.h +++ b/components/esp32/include/esp_wpa2.h @@ -24,35 +24,58 @@ extern "C" { /** * @brief Enable wpa2 enterprise authentication. * - * @attention wpa2 enterprise authentication can only be used when ESP32 station is enabled. - * wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. + * @attention 1. wpa2 enterprise authentication can only be used when ESP32 station is enabled. + * @attention 2. wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. * - * @return ESP_ERR_WIFI_OK: succeed. - * ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + * @return + * - ESP_ERR_WIFI_OK: succeed. + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) */ esp_err_t esp_wifi_sta_wpa2_ent_enable(void); /** * @brief Disable wpa2 enterprise authentication. * - * @attention wpa2 enterprise authentication can only be used when ESP32 station is enabled. - * wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. + * @attention 1. wpa2 enterprise authentication can only be used when ESP32 station is enabled. + * @attention 2. wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. * - * @return ESP_ERR_WIFI_OK: succeed. + * @return + * - ESP_ERR_WIFI_OK: succeed. */ esp_err_t esp_wifi_sta_wpa2_ent_disable(void); +/** + * @brief Set identity for PEAP/TTLS method. + * + * @attention The API only passes the parameter identity to the global pointer variable in wpa2 enterprise module. + * + * @param identity: point to address where stores the identity; + * @param len: length of identity, limited to 1~127 + * + * @return + * - ESP_ERR_WIFI_OK: succeed + * - ESP_ERR_WIFI_ARG: fail(len <= 0 or len >= 128) + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + */ +esp_err_t esp_wifi_sta_wpa2_ent_set_identity(unsigned char *identity, int len); + +/** + * @brief Clear identity for PEAP/TTLS method. + */ +void esp_wifi_sta_wpa2_ent_clear_identity(void); + /** * @brief Set username for PEAP/TTLS method. * * @attention The API only passes the parameter username to the global pointer variable in wpa2 enterprise module. * * @param username: point to address where stores the username; - * len: length of username, limited to 1~127 + * @param len: length of username, limited to 1~127 * - * @return ESP_ERR_WIFI_OK: succeed - * ESP_ERR_WIFI_ARG: fail(len <= 0 or len >= 128) - * ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + * @return + * - ESP_ERR_WIFI_OK: succeed + * - ESP_ERR_WIFI_ARG: fail(len <= 0 or len >= 128) + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) */ esp_err_t esp_wifi_sta_wpa2_ent_set_username(unsigned char *username, int len); @@ -67,11 +90,12 @@ void esp_wifi_sta_wpa2_ent_clear_username(void); * @attention The API only passes the parameter password to the global pointer variable in wpa2 enterprise module. * * @param password: point to address where stores the password; - * len: length of password(len > 0) + * @param len: length of password(len > 0) * - * @return ESP_ERR_WIFI_OK: succeed - * ESP_ERR_WIFI_ARG: fail(len <= 0) - * ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + * @return + * - ESP_ERR_WIFI_OK: succeed + * - ESP_ERR_WIFI_ARG: fail(len <= 0) + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) */ esp_err_t esp_wifi_sta_wpa2_ent_set_password(unsigned char *password, int len); @@ -83,15 +107,16 @@ void esp_wifi_sta_wpa2_ent_clear_password(void); /** * @brief Set new password for MSCHAPv2 method.. * - * @attention The API only passes the parameter password to the global pointer variable in wpa2 enterprise module. - * The new password is used to substitute the old password when eap-mschapv2 failure request message with error code ERROR_PASSWD_EXPIRED is received. + * @attention 1. The API only passes the parameter password to the global pointer variable in wpa2 enterprise module. + * @attention 2. The new password is used to substitute the old password when eap-mschapv2 failure request message with error code ERROR_PASSWD_EXPIRED is received. * * @param password: point to address where stores the password; - * len: length of password + * @param len: length of password * - * @return ESP_ERR_WIFI_OK: succeed - * ESP_ERR_WIFI_ARG: fail(len <= 0) - * ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + * @return + * - ESP_ERR_WIFI_OK: succeed + * - ESP_ERR_WIFI_ARG: fail(len <= 0) + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) */ esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(unsigned char *password, int len); @@ -104,13 +129,14 @@ void esp_wifi_sta_wpa2_ent_clear_new_password(void); /** * @brief Set CA certificate for PEAP/TTLS method. * - * @attention The API only passes the parameter ca_cert to the global pointer variable in wpa2 enterprise module. - * The ca_cert should be zero terminated. + * @attention 1. The API only passes the parameter ca_cert to the global pointer variable in wpa2 enterprise module. + * @attention 2. The ca_cert should be zero terminated. * * @param ca_cert: point to address where stores the CA certificate; - * len: length of ca_cert + * @param len: length of ca_cert * - * @return ESP_ERR_WIFI_OK: succeed + * @return + * - ESP_ERR_WIFI_OK: succeed */ esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(unsigned char *ca_cert, int len); @@ -122,17 +148,18 @@ void esp_wifi_sta_wpa2_ent_clear_ca_cert(void); /** * @brief Set client certificate and key. * - * @attention The API only passes the parameter client_cert, private_key and private_key_passwd to the global pointer variable in wpa2 enterprise module. - * The client_cert, private_key and private_key_passwd should be zero terminated. + * @attention 1. The API only passes the parameter client_cert, private_key and private_key_passwd to the global pointer variable in wpa2 enterprise module. + * @attention 2. The client_cert, private_key and private_key_passwd should be zero terminated. * * @param client_cert: point to address where stores the client certificate; - * client_cert_len: length of client certificate; - * private_key: point to address where stores the private key; - * private_key_len: length of private key, limited to 1~2048; - * private_key_password: point to address where stores the private key password; - * private_key_password_len: length of private key password; + * @param client_cert_len: length of client certificate; + * @param private_key: point to address where stores the private key; + * @param private_key_len: length of private key, limited to 1~2048; + * @param private_key_password: point to address where stores the private key password; + * @param private_key_password_len: length of private key password; * - * @return ESP_ERR_WIFI_OK: succeed + * @return + * - ESP_ERR_WIFI_OK: succeed */ esp_err_t esp_wifi_sta_wpa2_ent_set_cert_key(unsigned char *client_cert, int client_cert_len, unsigned char *private_key, int private_key_len, unsigned char *private_key_passwd, int private_key_passwd_len); @@ -145,9 +172,10 @@ void esp_wifi_sta_wpa2_ent_clear_cert_key(void); * @brief Set wpa2 enterprise certs time check(disable or not). * * @param true: disable wpa2 enterprise certs time check - * false: enable wpa2 enterprise certs time check + * @param false: enable wpa2 enterprise certs time check * - * @return ESP_OK: succeed + * @return + * - ESP_OK: succeed */ esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable); @@ -156,7 +184,8 @@ esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable); * * @param disable: store disable value * - * @return ESP_OK: succeed + * @return + * - ESP_OK: succeed */ esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable); diff --git a/components/esp32/include/rom/ets_sys.h b/components/esp32/include/rom/ets_sys.h index 690691675a..0f972f2c33 100644 --- a/components/esp32/include/rom/ets_sys.h +++ b/components/esp32/include/rom/ets_sys.h @@ -383,6 +383,18 @@ void ets_delay_us(uint32_t us); */ void ets_update_cpu_frequency(uint32_t ticks_per_us); +/** + * @brief Set the real CPU ticks per us to the ets, so that ets_delay_us will be accurate. + * + * @note This function only sets the tick rate for the current CPU. It is located in ROM, + * so the deep sleep stub can use it even if IRAM is not initialized yet. + * + * @param uint32_t ticks_per_us : CPU ticks per us. + * + * @return None + */ +void ets_update_cpu_frequency_rom(uint32_t ticks_per_us); + /** * @brief Get the real CPU ticks per us to the ets. * This function do not return real CPU ticks per us, just the record in ets. It can be used to check with the real CPU frequency. diff --git a/components/esp32/include/rom/rtc.h b/components/esp32/include/rom/rtc.h index 1ff7f033b9..9bcd9bd7e0 100644 --- a/components/esp32/include/rom/rtc.h +++ b/components/esp32/include/rom/rtc.h @@ -53,16 +53,18 @@ extern "C" { * Rtc store registers usage * RTC_CNTL_STORE0_REG * RTC_CNTL_STORE1_REG - * RTC_CNTL_STORE2_REG - * RTC_CNTL_STORE3_REG - * RTC_CNTL_STORE4_REG Reserved - * RTC_CNTL_STORE5_REG External Xtal Frequency + * RTC_CNTL_STORE2_REG Boot time, low word + * RTC_CNTL_STORE3_REG Boot time, high word + * RTC_CNTL_STORE4_REG External XTAL frequency + * RTC_CNTL_STORE5_REG APB bus frequency * RTC_CNTL_STORE6_REG FAST_RTC_MEMORY_ENTRY * RTC_CNTL_STORE7_REG FAST_RTC_MEMORY_CRC ************************************************************************************* */ -#define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG -#define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG +#define RTC_BOOT_TIME_LOW_REG RTC_CNTL_STORE2_REG +#define RTC_BOOT_TIME_HIGH_REG RTC_CNTL_STORE3_REG +#define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG +#define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG typedef enum { @@ -179,6 +181,9 @@ void set_rtc_memory_crc(void); /** * @brief Software Reset digital core. * + * It is not recommended to use this function in esp-idf, use + * esp_restart() instead. + * * @param None * * @return None @@ -188,6 +193,9 @@ void software_reset(void); /** * @brief Software Reset digital core. * + * It is not recommended to use this function in esp-idf, use + * esp_restart() instead. + * * @param int cpu_no : The CPU to reset, 0 for PRO CPU, 1 for APP CPU. * * @return None diff --git a/components/esp32/include/soc/cpu.h b/components/esp32/include/soc/cpu.h index aa471a1739..2e0ac7de80 100644 --- a/components/esp32/include/soc/cpu.h +++ b/components/esp32/include/soc/cpu.h @@ -104,4 +104,13 @@ void esp_cpu_stall(int cpu_id); */ void esp_cpu_unstall(int cpu_id); +/** + * @brief Returns true if a JTAG debugger is attached to CPU + * OCD (on chip debug) port. + * + * @note If "Make exception and panic handlers JTAG/OCD aware" + * is disabled, this function always returns false. + */ +bool esp_cpu_in_ocd_debug_mode(); + #endif diff --git a/components/esp32/include/soc/dport_reg.h b/components/esp32/include/soc/dport_reg.h index f84346717c..ef231e316e 100644 --- a/components/esp32/include/soc/dport_reg.h +++ b/components/esp32/include/soc/dport_reg.h @@ -1035,14 +1035,20 @@ #define DPORT_WIFI_CLK_EN_V 0xFFFFFFFF #define DPORT_WIFI_CLK_EN_S 0 -#define DPORT_WIFI_RST_EN_REG (DR_REG_DPORT_BASE + 0x0D0) -/* DPORT_WIFI_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ +#define DPORT_CORE_RST_EN_REG (DR_REG_DPORT_BASE + 0x0D0) +/* DPORT_CORE_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ /*description: */ +#define DPROT_RW_BTLP_RST (BIT(10)) +#define DPROT_RW_BTMAC_RST (BIT(9)) +#define DPORT_MACPWR_RST (BIT(8)) +#define DPORT_EMAC_RST (BIT(7)) +#define DPORT_SDIO_HOST_RST (BIT(6)) +#define DPORT_SDIO_RST (BIT(5)) +#define DPORT_BTMAC_RST (BIT(4)) +#define DPORT_BT_RST (BIT(3)) #define DPORT_MAC_RST (BIT(2)) -#define DPORT_WIFI_RST 0xFFFFFFFF -#define DPORT_WIFI_RST_M ((DPORT_WIFI_RST_V)<<(DPORT_WIFI_RST_S)) -#define DPORT_WIFI_RST_V 0xFFFFFFFF -#define DPORT_WIFI_RST_S 0 +#define DPORT_FE_RST (BIT(1)) +#define DPORT_BB_RST (BIT(0)) #define DPORT_BT_LPCK_DIV_INT_REG (DR_REG_DPORT_BASE + 0x0D4) /* DPORT_BTEXTWAKEUP_REQ : R/W ;bitpos:[12] ;default: 1'b0 ; */ diff --git a/components/esp32/include/soc/gpio_sig_map.h b/components/esp32/include/soc/gpio_sig_map.h index 4d2943fb9f..1d3dc5b04c 100644 --- a/components/esp32/include/soc/gpio_sig_map.h +++ b/components/esp32/include/soc/gpio_sig_map.h @@ -135,7 +135,7 @@ #define HSPICS2_IN_IDX 62 #define HSPICS2_OUT_IDX 62 #define VSPICLK_IN_IDX 63 -#define VSPICLK_OUT_MUX_IDX 63 +#define VSPICLK_OUT_IDX 63 #define VSPIQ_IN_IDX 64 #define VSPIQ_OUT_IDX 64 #define VSPID_IN_IDX 65 diff --git a/components/esp32/include/soc/i2c_reg.h b/components/esp32/include/soc/i2c_reg.h index d5c9858a92..9693ca5b59 100644 --- a/components/esp32/include/soc/i2c_reg.h +++ b/components/esp32/include/soc/i2c_reg.h @@ -261,6 +261,8 @@ #define I2C_RXFIFO_FULL_THRHD_V 0x1F #define I2C_RXFIFO_FULL_THRHD_S 0 +#define I2C_DATA_APB_REG(i) (0x60013000 + (i) * 0x14000 + 0x001c) + #define I2C_DATA_REG(i) (REG_I2C_BASE(i) + 0x001c) /* I2C_FIFO_RDATA : RO ;bitpos:[7:0] ;default: 8'b0 ; */ /*description: The register represent the byte data read from rxfifo when use apb fifo access*/ diff --git a/components/esp32/include/soc/i2c_struct.h b/components/esp32/include/soc/i2c_struct.h index a29a9c5286..ea50d6bee5 100644 --- a/components/esp32/include/soc/i2c_struct.h +++ b/components/esp32/include/soc/i2c_struct.h @@ -16,7 +16,7 @@ typedef volatile struct { union { struct { - uint32_t scl_low_period:14; /*This register is used to configure the low level width of SCL clock.*/ + uint32_t period:14; /*This register is used to configure the low level width of SCL clock.*/ uint32_t reserved14: 18; }; uint32_t val; @@ -58,7 +58,7 @@ typedef volatile struct { } status_reg; union { struct { - uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data.*/ + uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data, unit: APB clock cycle.*/ uint32_t reserved20:12; }; uint32_t val; @@ -282,7 +282,7 @@ typedef volatile struct { uint32_t reserved_f4; uint32_t date; /**/ uint32_t reserved_fc; - uint32_t fifo_start_addr; /*This the start address for ram when use apb nonfifo access.*/ + uint32_t ram_data[32]; /*This the start address for ram when use apb nonfifo access.*/ } i2c_dev_t; extern i2c_dev_t I2C0; extern i2c_dev_t I2C1; diff --git a/components/esp32/include/soc/sdmmc_reg.h b/components/esp32/include/soc/sdmmc_reg.h new file mode 100644 index 0000000000..d1b452d1d1 --- /dev/null +++ b/components/esp32/include/soc/sdmmc_reg.h @@ -0,0 +1,94 @@ +// Copyright 2015-2016 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 _SOC_SDMMC_REG_H_ +#define _SOC_SDMMC_REG_H_ +#include "soc.h" + +#define SDMMC_CTRL_REG (DR_REG_SDMMC_BASE + 0x00) +#define SDMMC_PWREN_REG (DR_REG_SDMMC_BASE + 0x04) +#define SDMMC_CLKDIV_REG (DR_REG_SDMMC_BASE + 0x08) +#define SDMMC_CLKSRC_REG (DR_REG_SDMMC_BASE + 0x0c) +#define SDMMC_CLKENA_REG (DR_REG_SDMMC_BASE + 0x10) +#define SDMMC_TMOUT_REG (DR_REG_SDMMC_BASE + 0x14) +#define SDMMC_CTYPE_REG (DR_REG_SDMMC_BASE + 0x18) +#define SDMMC_BLKSIZ_REG (DR_REG_SDMMC_BASE + 0x1c) +#define SDMMC_BYTCNT_REG (DR_REG_SDMMC_BASE + 0x20) +#define SDMMC_INTMASK_REG (DR_REG_SDMMC_BASE + 0x24) +#define SDMMC_CMDARG_REG (DR_REG_SDMMC_BASE + 0x28) +#define SDMMC_CMD_REG (DR_REG_SDMMC_BASE + 0x2c) +#define SDMMC_RESP0_REG (DR_REG_SDMMC_BASE + 0x30) +#define SDMMC_RESP1_REG (DR_REG_SDMMC_BASE + 0x34) +#define SDMMC_RESP2_REG (DR_REG_SDMMC_BASE + 0x38) +#define SDMMC_RESP3_REG (DR_REG_SDMMC_BASE + 0x3c) + +#define SDMMC_MINTSTS_REG (DR_REG_SDMMC_BASE + 0x40) +#define SDMMC_RINTSTS_REG (DR_REG_SDMMC_BASE + 0x44) +#define SDMMC_STATUS_REG (DR_REG_SDMMC_BASE + 0x48) +#define SDMMC_FIFOTH_REG (DR_REG_SDMMC_BASE + 0x4c) +#define SDMMC_CDETECT_REG (DR_REG_SDMMC_BASE + 0x50) +#define SDMMC_WRTPRT_REG (DR_REG_SDMMC_BASE + 0x54) +#define SDMMC_GPIO_REG (DR_REG_SDMMC_BASE + 0x58) +#define SDMMC_TCBCNT_REG (DR_REG_SDMMC_BASE + 0x5c) +#define SDMMC_TBBCNT_REG (DR_REG_SDMMC_BASE + 0x60) +#define SDMMC_DEBNCE_REG (DR_REG_SDMMC_BASE + 0x64) +#define SDMMC_USRID_REG (DR_REG_SDMMC_BASE + 0x68) +#define SDMMC_VERID_REG (DR_REG_SDMMC_BASE + 0x6c) +#define SDMMC_HCON_REG (DR_REG_SDMMC_BASE + 0x70) +#define SDMMC_UHS_REG_REG (DR_REG_SDMMC_BASE + 0x74) +#define SDMMC_RST_N_REG (DR_REG_SDMMC_BASE + 0x78) +#define SDMMC_BMOD_REG (DR_REG_SDMMC_BASE + 0x80) +#define SDMMC_PLDMND_REG (DR_REG_SDMMC_BASE + 0x84) +#define SDMMC_DBADDR_REG (DR_REG_SDMMC_BASE + 0x88) +#define SDMMC_DBADDRU_REG (DR_REG_SDMMC_BASE + 0x8c) +#define SDMMC_IDSTS_REG (DR_REG_SDMMC_BASE + 0x8c) +#define SDMMC_IDINTEN_REG (DR_REG_SDMMC_BASE + 0x90) +#define SDMMC_DSCADDR_REG (DR_REG_SDMMC_BASE + 0x94) +#define SDMMC_DSCADDRL_REG (DR_REG_SDMMC_BASE + 0x98) +#define SDMMC_DSCADDRU_REG (DR_REG_SDMMC_BASE + 0x9c) +#define SDMMC_BUFADDRL_REG (DR_REG_SDMMC_BASE + 0xa0) +#define SDMMC_BUFADDRU_REG (DR_REG_SDMMC_BASE + 0xa4) +#define SDMMC_CARDTHRCTL_REG (DR_REG_SDMMC_BASE + 0x100) +#define SDMMC_BACK_END_POWER_REG (DR_REG_SDMMC_BASE + 0x104) +#define SDMMC_UHS_REG_EXT_REG (DR_REG_SDMMC_BASE + 0x108) +#define SDMMC_EMMC_DDR_REG_REG (DR_REG_SDMMC_BASE + 0x10c) +#define SDMMC_ENABLE_SHIFT_REG (DR_REG_SDMMC_BASE + 0x110) + +#define SDMMC_CLOCK_REG (DR_REG_SDMMC_BASE + 0x800) + +#define SDMMC_INTMASK_EBE BIT(15) +#define SDMMC_INTMASK_ACD BIT(14) +#define SDMMC_INTMASK_SBE BIT(13) +#define SDMMC_INTMASK_HLE BIT(12) +#define SDMMC_INTMASK_FRUN BIT(11) +#define SDMMC_INTMASK_HTO BIT(10) +#define SDMMC_INTMASK_DTO BIT(9) +#define SDMMC_INTMASK_RTO BIT(8) +#define SDMMC_INTMASK_DCRC BIT(7) +#define SDMMC_INTMASK_RCRC BIT(6) +#define SDMMC_INTMASK_RXDR BIT(5) +#define SDMMC_INTMASK_TXDR BIT(4) +#define SDMMC_INTMASK_DATA_OVER BIT(3) +#define SDMMC_INTMASK_CMD_DONE BIT(2) +#define SDMMC_INTMASK_RESP_ERR BIT(1) +#define SDMMC_INTMASK_CD BIT(0) + +#define SDMMC_IDMAC_INTMASK_AI BIT(9) +#define SDMMC_IDMAC_INTMASK_NI BIT(8) +#define SDMMC_IDMAC_INTMASK_CES BIT(5) +#define SDMMC_IDMAC_INTMASK_DU BIT(4) +#define SDMMC_IDMAC_INTMASK_FBE BIT(2) +#define SDMMC_IDMAC_INTMASK_RI BIT(1) +#define SDMMC_IDMAC_INTMASK_TI BIT(0) + +#endif /* _SOC_SDMMC_REG_H_ */ diff --git a/components/esp32/include/soc/sdmmc_struct.h b/components/esp32/include/soc/sdmmc_struct.h new file mode 100644 index 0000000000..20bb9d260d --- /dev/null +++ b/components/esp32/include/soc/sdmmc_struct.h @@ -0,0 +1,371 @@ +// Copyright 2015-2016 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 _SOC_SDMMC_STRUCT_H_ +#define _SOC_SDMMC_STRUCT_H_ + +#include + +typedef struct { + uint32_t reserved1: 1; + uint32_t disable_int_on_completion: 1; + uint32_t last_descriptor: 1; + uint32_t first_descriptor: 1; + uint32_t second_address_chained: 1; + uint32_t end_of_ring: 1; + uint32_t reserved2: 24; + uint32_t card_error_summary: 1; + uint32_t owned_by_idmac: 1; + uint32_t buffer1_size: 13; + uint32_t buffer2_size: 13; + uint32_t reserved3: 6; + void* buffer1_ptr; + union { + void* buffer2_ptr; + void* next_desc_ptr; + }; +} sdmmc_desc_t; + +#define SDMMC_DMA_MAX_BUF_LEN 4096 + +_Static_assert(sizeof(sdmmc_desc_t) == 16, "invalid size of sdmmc_desc_t structure"); + + +typedef struct { + uint32_t cmd_index: 6; ///< Command index + uint32_t response_expect: 1; ///< set if response is expected + uint32_t response_long: 1; ///< 0: short response expected, 1: long response expected + uint32_t check_response_crc: 1; ///< set if controller should check response CRC + uint32_t data_expected: 1; ///< 0: no data expected, 1: data expected + uint32_t rw: 1; ///< 0: read from card, 1: write to card (don't care if no data expected) + uint32_t stream_mode: 1; ///< 0: block transfer, 1: stream transfer (don't care if no data expected) + uint32_t send_auto_stop: 1; ///< set to send stop at the end of the transfer + uint32_t wait_complete: 1; ///< 0: send command at once, 1: wait for previous command to complete + uint32_t stop_abort_cmd: 1; ///< set if this is a stop or abort command intended to stop current transfer + uint32_t send_init: 1; ///< set to send init sequence (80 clocks of 1) + uint32_t card_num: 5; ///< card number + uint32_t update_clk_reg: 1; ///< 0: normal command, 1: don't send command, just update clock registers + uint32_t read_ceata: 1; ///< set if performing read from CE-ATA device + uint32_t ccs_expected: 1; ///< set if CCS is expected from CE-ATA device + uint32_t enable_boot: 1; ///< set for mandatory boot mode + uint32_t expect_boot_ack: 1; ///< when set along with enable_boot, controller expects boot ack pattern + uint32_t disable_boot: 1; ///< set to terminate boot operation (don't set along with enable_boot) + uint32_t boot_mode: 1; ///< 0: mandatory boot operation, 1: alternate boot operation + uint32_t volt_switch: 1; ///< set to enable voltage switching (for CMD11 only) + uint32_t use_hold_reg: 1; ///< clear to bypass HOLD register + uint32_t reserved: 1; + uint32_t start_command: 1; ///< Start command; once command is sent to the card, bit is cleared. +} sdmmc_hw_cmd_t; ///< command format used in cmd register; this structure is defined to make it easier to build command values + +_Static_assert(sizeof(sdmmc_hw_cmd_t) == 4, "invalid size of sdmmc_cmd_t structure"); + + +typedef volatile struct { + union { + struct { + uint32_t controller_reset: 1; + uint32_t fifo_reset: 1; + uint32_t dma_reset: 1; + uint32_t reserved1: 1; + uint32_t int_enable: 1; + uint32_t dma_enable: 1; + uint32_t read_wait: 1; + uint32_t send_irq_response: 1; + uint32_t abort_read_data: 1; + uint32_t send_ccsd: 1; + uint32_t send_auto_stop_ccsd: 1; + uint32_t ceata_device_interrupt_status: 1; + uint32_t reserved2: 4; + uint32_t card_voltage_a: 4; + uint32_t card_voltage_b: 4; + uint32_t enable_od_pullup: 1; + uint32_t use_internal_dma: 1; + uint32_t reserved3: 6; + }; + uint32_t val; + } ctrl; + + uint32_t pwren; ///< 1: enable power to card, 0: disable power to card + + union { + struct { + uint32_t div0: 8; ///< 0: bypass, 1-255: divide clock by (2*div0). + uint32_t div1: 8; ///< 0: bypass, 1-255: divide clock by (2*div0). + uint32_t div2: 8; ///< 0: bypass, 1-255: divide clock by (2*div0). + uint32_t div3: 8; ///< 0: bypass, 1-255: divide clock by (2*div0). + }; + uint32_t val; + } clkdiv; + + union { + struct { + uint32_t card0: 2; ///< 0-3: select clock divider for card 0 among div0-div3 + uint32_t card1: 2; ///< 0-3: select clock divider for card 1 among div0-div3 + uint32_t reserved: 28; + }; + uint32_t val; + } clksrc; + + union { + struct { + uint32_t cclk_enable: 16; ///< 1: enable clock to card, 0: disable clock + uint32_t cclk_low_power: 16; ///< 1: enable clock gating when card is idle, 0: disable clock gating + }; + uint32_t val; + } clkena; + + union { + struct { + uint32_t response: 8; ///< response timeout, in card output clock cycles + uint32_t data: 24; ///< data read timeout, in card output clock cycles + }; + uint32_t val; + } tmout; + + union { + struct { + uint32_t card_width: 16; ///< one bit for each card: 0: 1-bit mode, 1: 4-bit mode + uint32_t card_width_8: 16; ///< one bit for each card: 0: not 8-bit mode (corresponding card_width bit is used), 1: 8-bit mode (card_width bit is ignored) + }; + uint32_t val; + } ctype; + + uint32_t blksiz: 16; ///< block size, default 0x200 + uint32_t : 16; + + uint32_t bytcnt; ///< number of bytes to be transferred + + union { + struct { + uint32_t cd: 1; ///< Card detect interrupt enable + uint32_t re: 1; ///< Response error interrupt enable + uint32_t cmd_done: 1; ///< Command done interrupt enable + uint32_t dto: 1; ///< Data transfer over interrupt enable + uint32_t txdr: 1; ///< Transmit FIFO data request interrupt enable + uint32_t rxdr: 1; ///< Receive FIFO data request interrupt enable + uint32_t rcrc: 1; ///< Response CRC error interrupt enable + uint32_t dcrc: 1; ///< Data CRC error interrupt enable + uint32_t rto: 1; ///< Response timeout interrupt enable + uint32_t drto: 1; ///< Data read timeout interrupt enable + uint32_t hto: 1; ///< Data starvation-by-host timeout interrupt enable + uint32_t frun: 1; ///< FIFO underrun/overrun error interrupt enable + uint32_t hle: 1; ///< Hardware locked write error interrupt enable + uint32_t sbi_bci: 1; ///< Start bit error / busy clear interrupt enable + uint32_t acd: 1; ///< Auto command done interrupt enable + uint32_t ebe: 1; ///< End bit error / write no CRC interrupt enable + uint32_t sdio: 16; ///< SDIO interrupt enable + }; + uint32_t val; + } intmask; + + uint32_t cmdarg; ///< Command argument to be passed to card + + sdmmc_hw_cmd_t cmd; + + uint32_t resp[4]; ///< Response from card + + union { + struct { + uint32_t cd: 1; ///< Card detect interrupt masked status + uint32_t re: 1; ///< Response error interrupt masked status + uint32_t cmd_done: 1; ///< Command done interrupt masked status + uint32_t dto: 1; ///< Data transfer over interrupt masked status + uint32_t txdr: 1; ///< Transmit FIFO data request interrupt masked status + uint32_t rxdr: 1; ///< Receive FIFO data request interrupt masked status + uint32_t rcrc: 1; ///< Response CRC error interrupt masked status + uint32_t dcrc: 1; ///< Data CRC error interrupt masked status + uint32_t rto: 1; ///< Response timeout interrupt masked status + uint32_t drto: 1; ///< Data read timeout interrupt masked status + uint32_t hto: 1; ///< Data starvation-by-host timeout interrupt masked status + uint32_t frun: 1; ///< FIFO underrun/overrun error interrupt masked status + uint32_t hle: 1; ///< Hardware locked write error interrupt masked status + uint32_t sbi_bci: 1; ///< Start bit error / busy clear interrupt masked status + uint32_t acd: 1; ///< Auto command done interrupt masked status + uint32_t ebe: 1; ///< End bit error / write no CRC interrupt masked status + uint32_t sdio: 16; ///< SDIO interrupt masked status + }; + uint32_t val; + } mintsts; + + union { + struct { + uint32_t cd: 1; ///< Card detect raw interrupt status + uint32_t re: 1; ///< Response error raw interrupt status + uint32_t cmd_done: 1; ///< Command done raw interrupt status + uint32_t dto: 1; ///< Data transfer over raw interrupt status + uint32_t txdr: 1; ///< Transmit FIFO data request raw interrupt status + uint32_t rxdr: 1; ///< Receive FIFO data request raw interrupt status + uint32_t rcrc: 1; ///< Response CRC error raw interrupt status + uint32_t dcrc: 1; ///< Data CRC error raw interrupt status + uint32_t rto: 1; ///< Response timeout raw interrupt status + uint32_t drto: 1; ///< Data read timeout raw interrupt status + uint32_t hto: 1; ///< Data starvation-by-host timeout raw interrupt status + uint32_t frun: 1; ///< FIFO underrun/overrun error raw interrupt status + uint32_t hle: 1; ///< Hardware locked write error raw interrupt status + uint32_t sbi_bci: 1; ///< Start bit error / busy clear raw interrupt status + uint32_t acd: 1; ///< Auto command done raw interrupt status + uint32_t ebe: 1; ///< End bit error / write no CRC raw interrupt status + uint32_t sdio: 16; ///< SDIO raw interrupt status + }; + uint32_t val; + } rintsts; ///< interrupts can be cleared by writing this register + + union { + struct { + uint32_t fifo_rx_watermark: 1; ///< FIFO reached receive watermark level + uint32_t fifo_tx_watermark: 1; ///< FIFO reached transmit watermark level + uint32_t fifo_empty: 1; ///< FIFO is empty + uint32_t fifo_full: 1; ///< FIFO is full + uint32_t cmd_fsm_state: 4; ///< command FSM state + uint32_t data3_status: 1; ///< this bit reads 1 if card is present + uint32_t data_busy: 1; ///< this bit reads 1 if card is busy + uint32_t data_fsm_busy: 1; ///< this bit reads 1 if transmit/receive FSM is busy + uint32_t response_index: 6; ///< index of the previous response + uint32_t fifo_count: 13; ///< number of filled locations in the FIFO + uint32_t dma_ack: 1; ///< DMA acknowledge signal + uint32_t dma_req: 1; ///< DMA request signal + }; + uint32_t val; + } status; + + union { + struct { + uint32_t tx_watermark: 12; ///< FIFO TX watermark level + uint32_t reserved1: 4; + uint32_t rx_watermark: 12; ///< FIFO RX watermark level + uint32_t dw_dma_mts: 3; + uint32_t reserved2: 1; + }; + uint32_t val; + } fifoth; + + union { + struct { + uint32_t cards: 2; ///< bit N reads 1 if card N is present + uint32_t reserved: 30; + }; + uint32_t val; + } cdetect; + + union { + struct { + uint32_t card0: 2; ///< bit N reads 1 if card N is write protected + uint32_t reserved: 30; + }; + uint32_t val; + } wrtprt; + + uint32_t gpio; ///< unused + uint32_t tcbcnt; ///< transferred (to card) byte count + uint32_t tbbcnt; ///< transferred from host to FIFO byte count + + union { + struct { + uint32_t debounce_count: 24; ///< number of host cycles used by debounce filter, typical time should be 5-25ms + uint32_t reserved: 8; + }; + } debnce; + + uint32_t usrid; ///< user ID + uint32_t verid; ///< IP block version + uint32_t hcon; ///< compile-time IP configuration + uint32_t uhs; ///< TBD + + union { + struct { + uint32_t cards: 2; ///< bit N resets card N, active low + uint32_t reserved: 30; + }; + } rst_n; + + uint32_t reserved_7c; + + union { + struct { + uint32_t sw_reset: 1; ///< set to reset DMA controller + uint32_t fb: 1; ///< set if AHB master performs fixed burst transfers + uint32_t dsl: 5; ///< descriptor skip length: number of words to skip between two unchained descriptors + uint32_t enable: 1; ///< set to enable IDMAC + uint32_t pbl: 3; ///< programmable burst length + uint32_t reserved: 21; + }; + uint32_t val; + } bmod; + + uint32_t pldmnd; ///< set any bit to resume IDMAC FSM from suspended state + sdmmc_desc_t* dbaddr; ///< descriptor list base + + union { + struct { + uint32_t ti: 1; ///< transmit interrupt status + uint32_t ri: 1; ///< receive interrupt status + uint32_t fbe: 1; ///< fatal bus error + uint32_t reserved1: 1; + uint32_t du: 1; ///< descriptor unavailable + uint32_t ces: 1; ///< card error summary + uint32_t reserved2: 2; + uint32_t nis: 1; ///< normal interrupt summary + uint32_t fbe_code: 3; ///< code of fatal bus error + uint32_t fsm: 4; ///< DMAC FSM state + uint32_t reserved3: 15; + }; + uint32_t val; + } idsts; + + union { + struct { + uint32_t ti: 1; ///< transmit interrupt enable + uint32_t ri: 1; ///< receive interrupt enable + uint32_t fbe: 1; ///< fatal bus error interrupt enable + uint32_t reserved1: 1; + uint32_t du: 1; ///< descriptor unavailable interrupt enable + uint32_t ces: 1; ///< card error interrupt enable + uint32_t reserved2: 2; + uint32_t ni: 1; ///< normal interrupt interrupt enable + uint32_t ai: 1; ///< abnormal interrupt enable + uint32_t reserved3: 22; + }; + uint32_t val; + } idinten; + + uint32_t dscaddr; ///< current host descriptor address + uint32_t dscaddrl; ///< unused + uint32_t dscaddru; ///< unused + uint32_t bufaddrl; ///< unused + uint32_t bufaddru; ///< unused + uint32_t reserved_a8[22]; + uint32_t cardthrctl; + uint32_t back_end_power; + uint32_t uhs_reg_ext; + uint32_t emmc_ddr_reg; + uint32_t enable_shift; + uint32_t reserved_114[443]; + union { + struct { + uint32_t phase_dout: 3; ///< phase of data output clock (0x0: 0, 0x1: 90, 0x4: 180, 0x6: 270) + uint32_t phase_din: 3; ///< phase of data input clock + uint32_t phase_core: 3; ///< phase of the clock to SDMMC peripheral + uint32_t div_factor_p: 4; ///< controls clock period; it will be (div_factor_p + 1) / 160MHz + uint32_t div_factor_h: 4; ///< controls length of high pulse; it will be (div_factor_h + 1) / 160MHz + uint32_t div_factor_m: 4; ///< should be equal to div_factor_p + }; + uint32_t val; + } clock; +} sdmmc_dev_t; +extern sdmmc_dev_t SDMMC; + +_Static_assert(sizeof(sdmmc_dev_t) == 0x804, "invalid size of sdmmc_dev_t structure"); + + + +#endif //_SOC_SDMMC_STRUCT_H_ diff --git a/components/esp32/include/soc/soc.h b/components/esp32/include/soc/soc.h index b93bae7298..3e0360e249 100755 --- a/components/esp32/include/soc/soc.h +++ b/components/esp32/include/soc/soc.h @@ -153,11 +153,12 @@ #define DR_REG_FRC_TIMER_BASE 0x3ff47000 #define DR_REG_RTCCNTL_BASE 0x3ff48000 #define DR_REG_RTCIO_BASE 0x3ff48400 -#define DR_REG_SENS_BASE 0x3ff48800 +#define DR_REG_SENS_BASE 0x3ff48800 #define DR_REG_IO_MUX_BASE 0x3ff49000 #define DR_REG_RTCMEM0_BASE 0x3ff61000 #define DR_REG_RTCMEM1_BASE 0x3ff62000 #define DR_REG_RTCMEM2_BASE 0x3ff63000 +#define DR_REG_SYSCON_BASE 0x3ff66000 #define DR_REG_HINF_BASE 0x3ff4B000 #define DR_REG_UHCI1_BASE 0x3ff4C000 #define DR_REG_I2S_BASE 0x3ff4F000 diff --git a/components/esp32/include/soc/spi_reg.h b/components/esp32/include/soc/spi_reg.h index d1eeedb9f0..0cc27b8994 100644 --- a/components/esp32/include/soc/spi_reg.h +++ b/components/esp32/include/soc/spi_reg.h @@ -133,12 +133,8 @@ #define SPI_FLASH_PER_S 16 #define SPI_ADDR_REG(i) (REG_SPI_BASE(i) + 0x4) -/* SPI_USR_ADDR_VALUE : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ -/*description: [31:8]:address to slave [7:0]:Reserved.*/ -#define SPI_USR_ADDR_VALUE 0xFFFFFFFF -#define SPI_USR_ADDR_VALUE_M ((SPI_USR_ADDR_VALUE_V)<<(SPI_USR_ADDR_VALUE_S)) -#define SPI_USR_ADDR_VALUE_V 0xFFFFFFFF -#define SPI_USR_ADDR_VALUE_S 0 +//The CSV actually is wrong here. It indicates that the lower 8 bits of this register are reserved. This is not true, +//all 32 bits of SPI_ADDR_REG are usable/used. #define SPI_CTRL_REG(i) (REG_SPI_BASE(i) + 0x8) /* SPI_WR_BIT_ORDER : R/W ;bitpos:[26] ;default: 1'b0 ; */ @@ -601,19 +597,19 @@ #define SPI_CK_IDLE_EDGE_M (BIT(29)) #define SPI_CK_IDLE_EDGE_V 0x1 #define SPI_CK_IDLE_EDGE_S 29 -/* SPI_MASTER_CK_SEL : R/W ;bitpos:[15:11] ;default: 5'b0 ; */ +/* SPI_MASTER_CK_SEL : R/W ;bitpos:[13:11] ;default: 3'b0 ; */ /*description: In the master mode spi cs line is enable as spi clk it is combined with spi_cs0_dis spi_cs1_dis spi_cs2_dis.*/ -#define SPI_MASTER_CK_SEL 0x0000001F +#define SPI_MASTER_CK_SEL 0x00000007 #define SPI_MASTER_CK_SEL_M ((SPI_MASTER_CK_SEL_V)<<(SPI_MASTER_CK_SEL_S)) -#define SPI_MASTER_CK_SEL_V 0x1F +#define SPI_MASTER_CK_SEL_V 0x07 #define SPI_MASTER_CK_SEL_S 11 -/* SPI_MASTER_CS_POL : R/W ;bitpos:[10:6] ;default: 5'b0 ; */ +/* SPI_MASTER_CS_POL : R/W ;bitpos:[8:6] ;default: 3'b0 ; */ /*description: In the master mode the bits are the polarity of spi cs line the value is equivalent to spi_cs ^ spi_master_cs_pol.*/ -#define SPI_MASTER_CS_POL 0x0000001F +#define SPI_MASTER_CS_POL 0x00000007 #define SPI_MASTER_CS_POL_M ((SPI_MASTER_CS_POL_V)<<(SPI_MASTER_CS_POL_S)) -#define SPI_MASTER_CS_POL_V 0x1F +#define SPI_MASTER_CS_POL_V 0x7 #define SPI_MASTER_CS_POL_S 6 /* SPI_CK_DIS : R/W ;bitpos:[5] ;default: 1'b0 ; */ /*description: 1: spi clk out disable 0: spi clk out enable*/ diff --git a/components/esp32/include/soc/spi_struct.h b/components/esp32/include/soc/spi_struct.h index c7aa29a7c9..149782ff19 100644 --- a/components/esp32/include/soc/spi_struct.h +++ b/components/esp32/include/soc/spi_struct.h @@ -36,13 +36,7 @@ typedef volatile struct { }; uint32_t val; } cmd; - union { - struct { - uint32_t reserved : 8; - uint32_t usr_addr_value:24; /*[31:8]:address to slave [7:0]:Reserved.*/ - }; - uint32_t val; - } addr; + uint32_t addr; /*addr to slave / from master */ union { struct { uint32_t reserved0: 10; /*reserved*/ @@ -177,9 +171,10 @@ typedef volatile struct { uint32_t cs2_dis: 1; /*SPI CS2 pin enable, 1: disable CS2, 0: spi_cs2 signal is from/to CS2 pin*/ uint32_t reserved3: 2; /*reserved*/ uint32_t ck_dis: 1; /*1: spi clk out disable 0: spi clk out enable*/ - uint32_t master_cs_pol: 5; /*In the master mode the bits are the polarity of spi cs line the value is equivalent to spi_cs ^ spi_master_cs_pol.*/ - uint32_t master_ck_sel: 5; /*In the master mode spi cs line is enable as spi clk it is combined with spi_cs0_dis spi_cs1_dis spi_cs2_dis.*/ - uint32_t reserved16: 13; /*reserved*/ + uint32_t master_cs_pol: 3; /*In the master mode the bits are the polarity of spi cs line the value is equivalent to spi_cs ^ spi_master_cs_pol.*/ + uint32_t reserved9: 2; /*reserved*/ + uint32_t master_ck_sel: 3; /*In the master mode spi cs line is enable as spi clk it is combined with spi_cs0_dis spi_cs1_dis spi_cs2_dis.*/ + uint32_t reserved14: 15; /*reserved*/ uint32_t ck_idle_edge: 1; /*1: spi clk line is high when idle 0: spi clk line is low when idle*/ uint32_t cs_keep_active: 1; /*spi cs line keep low when the bit is set.*/ uint32_t reserved31: 1; /*reserved*/ @@ -193,7 +188,11 @@ typedef volatile struct { uint32_t rd_sta_done: 1; /*The interrupt raw bit for the completion of read-status operation in the slave mode.*/ uint32_t wr_sta_done: 1; /*The interrupt raw bit for the completion of write-status operation in the slave mode.*/ uint32_t trans_done: 1; /*The interrupt raw bit for the completion of any operation in both the master mode and the slave mode.*/ - uint32_t int_en: 5; /*Interrupt enable bits for the below 5 sources*/ + uint32_t rd_buf_inten: 1; /*The interrupt enable bit for the completion of read-buffer operation in the slave mode.*/ + uint32_t wr_buf_inten: 1; /*The interrupt enable bit for the completion of write-buffer operation in the slave mode.*/ + uint32_t rd_sta_inten: 1; /*The interrupt enable bit for the completion of read-status operation in the slave mode.*/ + uint32_t wr_sta_inten: 1; /*The interrupt enable bit for the completion of write-status operation in the slave mode.*/ + uint32_t trans_inten: 1; /*The interrupt enable bit for the completion of any operation in both the master mode and the slave mode.*/ uint32_t cs_i_mode: 2; /*In the slave mode this bits used to synchronize the input spi cs signal and eliminate spi cs jitter.*/ uint32_t reserved12: 5; /*reserved*/ uint32_t last_command: 3; /*In the slave mode it is the value of command.*/ diff --git a/components/esp32/include/soc/syscon_reg.h b/components/esp32/include/soc/syscon_reg.h new file mode 100644 index 0000000000..5012b27e57 --- /dev/null +++ b/components/esp32/include/soc/syscon_reg.h @@ -0,0 +1,294 @@ +// Copyright 2015-2016 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 _SOC_SYSCON_REG_H_ +#define _SOC_SYSCON_REG_H_ + +#include "soc.h" +#define SYSCON_SYSCLK_CONF_REG (DR_REG_SYSCON_BASE + 0x0) +/* SYSCON_QUICK_CLK_CHNG : R/W ;bitpos:[13] ;default: 1'b1 ; */ +/*description: */ +#define SYSCON_QUICK_CLK_CHNG (BIT(13)) +#define SYSCON_QUICK_CLK_CHNG_M (BIT(13)) +#define SYSCON_QUICK_CLK_CHNG_V 0x1 +#define SYSCON_QUICK_CLK_CHNG_S 13 +/* SYSCON_RST_TICK_CNT : R/W ;bitpos:[12] ;default: 1'b0 ; */ +/*description: */ +#define SYSCON_RST_TICK_CNT (BIT(12)) +#define SYSCON_RST_TICK_CNT_M (BIT(12)) +#define SYSCON_RST_TICK_CNT_V 0x1 +#define SYSCON_RST_TICK_CNT_S 12 +/* SYSCON_CLK_EN : R/W ;bitpos:[11] ;default: 1'b0 ; */ +/*description: */ +#define SYSCON_CLK_EN (BIT(11)) +#define SYSCON_CLK_EN_M (BIT(11)) +#define SYSCON_CLK_EN_V 0x1 +#define SYSCON_CLK_EN_S 11 +/* SYSCON_CLK_320M_EN : R/W ;bitpos:[10] ;default: 1'b0 ; */ +/*description: */ +#define SYSCON_CLK_320M_EN (BIT(10)) +#define SYSCON_CLK_320M_EN_M (BIT(10)) +#define SYSCON_CLK_320M_EN_V 0x1 +#define SYSCON_CLK_320M_EN_S 10 +/* SYSCON_PRE_DIV_CNT : R/W ;bitpos:[9:0] ;default: 10'h0 ; */ +/*description: */ +#define SYSCON_PRE_DIV_CNT 0x000003FF +#define SYSCON_PRE_DIV_CNT_M ((SYSCON_PRE_DIV_CNT_V)<<(SYSCON_PRE_DIV_CNT_S)) +#define SYSCON_PRE_DIV_CNT_V 0x3FF +#define SYSCON_PRE_DIV_CNT_S 0 + +#define SYSCON_XTAL_TICK_CONF_REG (DR_REG_SYSCON_BASE + 0x4) +/* SYSCON_XTAL_TICK_NUM : R/W ;bitpos:[7:0] ;default: 8'd39 ; */ +/*description: */ +#define SYSCON_XTAL_TICK_NUM 0x000000FF +#define SYSCON_XTAL_TICK_NUM_M ((SYSCON_XTAL_TICK_NUM_V)<<(SYSCON_XTAL_TICK_NUM_S)) +#define SYSCON_XTAL_TICK_NUM_V 0xFF +#define SYSCON_XTAL_TICK_NUM_S 0 + +#define SYSCON_PLL_TICK_CONF_REG (DR_REG_SYSCON_BASE + 0x8) +/* SYSCON_PLL_TICK_NUM : R/W ;bitpos:[7:0] ;default: 8'd79 ; */ +/*description: */ +#define SYSCON_PLL_TICK_NUM 0x000000FF +#define SYSCON_PLL_TICK_NUM_M ((SYSCON_PLL_TICK_NUM_V)<<(SYSCON_PLL_TICK_NUM_S)) +#define SYSCON_PLL_TICK_NUM_V 0xFF +#define SYSCON_PLL_TICK_NUM_S 0 + +#define SYSCON_CK8M_TICK_CONF_REG (DR_REG_SYSCON_BASE + 0xC) +/* SYSCON_CK8M_TICK_NUM : R/W ;bitpos:[7:0] ;default: 8'd11 ; */ +/*description: */ +#define SYSCON_CK8M_TICK_NUM 0x000000FF +#define SYSCON_CK8M_TICK_NUM_M ((SYSCON_CK8M_TICK_NUM_V)<<(SYSCON_CK8M_TICK_NUM_S)) +#define SYSCON_CK8M_TICK_NUM_V 0xFF +#define SYSCON_CK8M_TICK_NUM_S 0 + +#define SYSCON_SARADC_CTRL_REG (DR_REG_SYSCON_BASE + 0x10) +/* SYSCON_SARADC_DATA_TO_I2S : R/W ;bitpos:[26] ;default: 1'b0 ; */ +/*description: 1: I2S input data is from SAR ADC (for DMA) 0: I2S input data + is from GPIO matrix*/ +#define SYSCON_SARADC_DATA_TO_I2S (BIT(26)) +#define SYSCON_SARADC_DATA_TO_I2S_M (BIT(26)) +#define SYSCON_SARADC_DATA_TO_I2S_V 0x1 +#define SYSCON_SARADC_DATA_TO_I2S_S 26 +/* SYSCON_SARADC_DATA_SAR_SEL : R/W ;bitpos:[25] ;default: 1'b0 ; */ +/*description: 1: sar_sel will be coded by the MSB of the 16-bit output data + in this case the resolution should not be larger than 11 bits.*/ +#define SYSCON_SARADC_DATA_SAR_SEL (BIT(25)) +#define SYSCON_SARADC_DATA_SAR_SEL_M (BIT(25)) +#define SYSCON_SARADC_DATA_SAR_SEL_V 0x1 +#define SYSCON_SARADC_DATA_SAR_SEL_S 25 +/* SYSCON_SARADC_SAR2_PATT_P_CLEAR : R/W ;bitpos:[24] ;default: 1'd0 ; */ +/*description: clear the pointer of pattern table for DIG ADC2 CTRL*/ +#define SYSCON_SARADC_SAR2_PATT_P_CLEAR (BIT(24)) +#define SYSCON_SARADC_SAR2_PATT_P_CLEAR_M (BIT(24)) +#define SYSCON_SARADC_SAR2_PATT_P_CLEAR_V 0x1 +#define SYSCON_SARADC_SAR2_PATT_P_CLEAR_S 24 +/* SYSCON_SARADC_SAR1_PATT_P_CLEAR : R/W ;bitpos:[23] ;default: 1'd0 ; */ +/*description: clear the pointer of pattern table for DIG ADC1 CTRL*/ +#define SYSCON_SARADC_SAR1_PATT_P_CLEAR (BIT(23)) +#define SYSCON_SARADC_SAR1_PATT_P_CLEAR_M (BIT(23)) +#define SYSCON_SARADC_SAR1_PATT_P_CLEAR_V 0x1 +#define SYSCON_SARADC_SAR1_PATT_P_CLEAR_S 23 +/* SYSCON_SARADC_SAR2_PATT_LEN : R/W ;bitpos:[22:19] ;default: 4'd15 ; */ +/*description: 0 ~ 15 means length 1 ~ 16*/ +#define SYSCON_SARADC_SAR2_PATT_LEN 0x0000000F +#define SYSCON_SARADC_SAR2_PATT_LEN_M ((SYSCON_SARADC_SAR2_PATT_LEN_V)<<(SYSCON_SARADC_SAR2_PATT_LEN_S)) +#define SYSCON_SARADC_SAR2_PATT_LEN_V 0xF +#define SYSCON_SARADC_SAR2_PATT_LEN_S 19 +/* SYSCON_SARADC_SAR1_PATT_LEN : R/W ;bitpos:[18:15] ;default: 4'd15 ; */ +/*description: 0 ~ 15 means length 1 ~ 16*/ +#define SYSCON_SARADC_SAR1_PATT_LEN 0x0000000F +#define SYSCON_SARADC_SAR1_PATT_LEN_M ((SYSCON_SARADC_SAR1_PATT_LEN_V)<<(SYSCON_SARADC_SAR1_PATT_LEN_S)) +#define SYSCON_SARADC_SAR1_PATT_LEN_V 0xF +#define SYSCON_SARADC_SAR1_PATT_LEN_S 15 +/* SYSCON_SARADC_SAR_CLK_DIV : R/W ;bitpos:[14:7] ;default: 8'd4 ; */ +/*description: SAR clock divider*/ +#define SYSCON_SARADC_SAR_CLK_DIV 0x000000FF +#define SYSCON_SARADC_SAR_CLK_DIV_M ((SYSCON_SARADC_SAR_CLK_DIV_V)<<(SYSCON_SARADC_SAR_CLK_DIV_S)) +#define SYSCON_SARADC_SAR_CLK_DIV_V 0xFF +#define SYSCON_SARADC_SAR_CLK_DIV_S 7 +/* SYSCON_SARADC_SAR_CLK_GATED : R/W ;bitpos:[6] ;default: 1'b1 ; */ +/*description: */ +#define SYSCON_SARADC_SAR_CLK_GATED (BIT(6)) +#define SYSCON_SARADC_SAR_CLK_GATED_M (BIT(6)) +#define SYSCON_SARADC_SAR_CLK_GATED_V 0x1 +#define SYSCON_SARADC_SAR_CLK_GATED_S 6 +/* SYSCON_SARADC_SAR_SEL : R/W ;bitpos:[5] ;default: 1'd0 ; */ +/*description: 0: SAR1 1: SAR2 only work for single SAR mode*/ +#define SYSCON_SARADC_SAR_SEL (BIT(5)) +#define SYSCON_SARADC_SAR_SEL_M (BIT(5)) +#define SYSCON_SARADC_SAR_SEL_V 0x1 +#define SYSCON_SARADC_SAR_SEL_S 5 +/* SYSCON_SARADC_WORK_MODE : R/W ;bitpos:[4:3] ;default: 2'd0 ; */ +/*description: 0: single mode 1: double mode 2: alternate mode*/ +#define SYSCON_SARADC_WORK_MODE 0x00000003 +#define SYSCON_SARADC_WORK_MODE_M ((SYSCON_SARADC_WORK_MODE_V)<<(SYSCON_SARADC_WORK_MODE_S)) +#define SYSCON_SARADC_WORK_MODE_V 0x3 +#define SYSCON_SARADC_WORK_MODE_S 3 +/* SYSCON_SARADC_SAR2_MUX : R/W ;bitpos:[2] ;default: 1'd0 ; */ +/*description: 1: SAR ADC2 is controlled by DIG ADC2 CTRL 0: SAR ADC2 is controlled + by PWDET CTRL*/ +#define SYSCON_SARADC_SAR2_MUX (BIT(2)) +#define SYSCON_SARADC_SAR2_MUX_M (BIT(2)) +#define SYSCON_SARADC_SAR2_MUX_V 0x1 +#define SYSCON_SARADC_SAR2_MUX_S 2 +/* SYSCON_SARADC_START : R/W ;bitpos:[1] ;default: 1'd0 ; */ +/*description: */ +#define SYSCON_SARADC_START (BIT(1)) +#define SYSCON_SARADC_START_M (BIT(1)) +#define SYSCON_SARADC_START_V 0x1 +#define SYSCON_SARADC_START_S 1 +/* SYSCON_SARADC_START_FORCE : R/W ;bitpos:[0] ;default: 1'd0 ; */ +/*description: */ +#define SYSCON_SARADC_START_FORCE (BIT(0)) +#define SYSCON_SARADC_START_FORCE_M (BIT(0)) +#define SYSCON_SARADC_START_FORCE_V 0x1 +#define SYSCON_SARADC_START_FORCE_S 0 + +#define SYSCON_SARADC_CTRL2_REG (DR_REG_SYSCON_BASE + 0x14) +/* SYSCON_SARADC_SAR2_INV : R/W ;bitpos:[10] ;default: 1'd0 ; */ +/*description: 1: data to DIG ADC2 CTRL is inverted otherwise not*/ +#define SYSCON_SARADC_SAR2_INV (BIT(10)) +#define SYSCON_SARADC_SAR2_INV_M (BIT(10)) +#define SYSCON_SARADC_SAR2_INV_V 0x1 +#define SYSCON_SARADC_SAR2_INV_S 10 +/* SYSCON_SARADC_SAR1_INV : R/W ;bitpos:[9] ;default: 1'd0 ; */ +/*description: 1: data to DIG ADC1 CTRL is inverted otherwise not*/ +#define SYSCON_SARADC_SAR1_INV (BIT(9)) +#define SYSCON_SARADC_SAR1_INV_M (BIT(9)) +#define SYSCON_SARADC_SAR1_INV_V 0x1 +#define SYSCON_SARADC_SAR1_INV_S 9 +/* SYSCON_SARADC_MAX_MEAS_NUM : R/W ;bitpos:[8:1] ;default: 8'd255 ; */ +/*description: max conversion number*/ +#define SYSCON_SARADC_MAX_MEAS_NUM 0x000000FF +#define SYSCON_SARADC_MAX_MEAS_NUM_M ((SYSCON_SARADC_MAX_MEAS_NUM_V)<<(SYSCON_SARADC_MAX_MEAS_NUM_S)) +#define SYSCON_SARADC_MAX_MEAS_NUM_V 0xFF +#define SYSCON_SARADC_MAX_MEAS_NUM_S 1 +/* SYSCON_SARADC_MEAS_NUM_LIMIT : R/W ;bitpos:[0] ;default: 1'd0 ; */ +/*description: */ +#define SYSCON_SARADC_MEAS_NUM_LIMIT (BIT(0)) +#define SYSCON_SARADC_MEAS_NUM_LIMIT_M (BIT(0)) +#define SYSCON_SARADC_MEAS_NUM_LIMIT_V 0x1 +#define SYSCON_SARADC_MEAS_NUM_LIMIT_S 0 + +#define SYSCON_SARADC_FSM_REG (DR_REG_SYSCON_BASE + 0x18) +/* SYSCON_SARADC_SAMPLE_CYCLE : R/W ;bitpos:[31:24] ;default: 8'd2 ; */ +/*description: sample cycles*/ +#define SYSCON_SARADC_SAMPLE_CYCLE 0x000000FF +#define SYSCON_SARADC_SAMPLE_CYCLE_M ((SYSCON_SARADC_SAMPLE_CYCLE_V)<<(SYSCON_SARADC_SAMPLE_CYCLE_S)) +#define SYSCON_SARADC_SAMPLE_CYCLE_V 0xFF +#define SYSCON_SARADC_SAMPLE_CYCLE_S 24 +/* SYSCON_SARADC_START_WAIT : R/W ;bitpos:[23:16] ;default: 8'd8 ; */ +/*description: */ +#define SYSCON_SARADC_START_WAIT 0x000000FF +#define SYSCON_SARADC_START_WAIT_M ((SYSCON_SARADC_START_WAIT_V)<<(SYSCON_SARADC_START_WAIT_S)) +#define SYSCON_SARADC_START_WAIT_V 0xFF +#define SYSCON_SARADC_START_WAIT_S 16 +/* SYSCON_SARADC_STANDBY_WAIT : R/W ;bitpos:[15:8] ;default: 8'd255 ; */ +/*description: */ +#define SYSCON_SARADC_STANDBY_WAIT 0x000000FF +#define SYSCON_SARADC_STANDBY_WAIT_M ((SYSCON_SARADC_STANDBY_WAIT_V)<<(SYSCON_SARADC_STANDBY_WAIT_S)) +#define SYSCON_SARADC_STANDBY_WAIT_V 0xFF +#define SYSCON_SARADC_STANDBY_WAIT_S 8 +/* SYSCON_SARADC_RSTB_WAIT : R/W ;bitpos:[7:0] ;default: 8'd8 ; */ +/*description: */ +#define SYSCON_SARADC_RSTB_WAIT 0x000000FF +#define SYSCON_SARADC_RSTB_WAIT_M ((SYSCON_SARADC_RSTB_WAIT_V)<<(SYSCON_SARADC_RSTB_WAIT_S)) +#define SYSCON_SARADC_RSTB_WAIT_V 0xFF +#define SYSCON_SARADC_RSTB_WAIT_S 0 + +#define SYSCON_SARADC_SAR1_PATT_TAB1_REG (DR_REG_SYSCON_BASE + 0x1C) +/* SYSCON_SARADC_SAR1_PATT_TAB1 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: item 0 ~ 3 for pattern table 1 (each item one byte)*/ +#define SYSCON_SARADC_SAR1_PATT_TAB1 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB1_M ((SYSCON_SARADC_SAR1_PATT_TAB1_V)<<(SYSCON_SARADC_SAR1_PATT_TAB1_S)) +#define SYSCON_SARADC_SAR1_PATT_TAB1_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB1_S 0 + +#define SYSCON_SARADC_SAR1_PATT_TAB2_REG (DR_REG_SYSCON_BASE + 0x20) +/* SYSCON_SARADC_SAR1_PATT_TAB2 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 4 ~ 7 for pattern table 1 (each item one byte)*/ +#define SYSCON_SARADC_SAR1_PATT_TAB2 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB2_M ((SYSCON_SARADC_SAR1_PATT_TAB2_V)<<(SYSCON_SARADC_SAR1_PATT_TAB2_S)) +#define SYSCON_SARADC_SAR1_PATT_TAB2_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB2_S 0 + +#define SYSCON_SARADC_SAR1_PATT_TAB3_REG (DR_REG_SYSCON_BASE + 0x24) +/* SYSCON_SARADC_SAR1_PATT_TAB3 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 8 ~ 11 for pattern table 1 (each item one byte)*/ +#define SYSCON_SARADC_SAR1_PATT_TAB3 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB3_M ((SYSCON_SARADC_SAR1_PATT_TAB3_V)<<(SYSCON_SARADC_SAR1_PATT_TAB3_S)) +#define SYSCON_SARADC_SAR1_PATT_TAB3_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB3_S 0 + +#define SYSCON_SARADC_SAR1_PATT_TAB4_REG (DR_REG_SYSCON_BASE + 0x28) +/* SYSCON_SARADC_SAR1_PATT_TAB4 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 12 ~ 15 for pattern table 1 (each item one byte)*/ +#define SYSCON_SARADC_SAR1_PATT_TAB4 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB4_M ((SYSCON_SARADC_SAR1_PATT_TAB4_V)<<(SYSCON_SARADC_SAR1_PATT_TAB4_S)) +#define SYSCON_SARADC_SAR1_PATT_TAB4_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB4_S 0 + +#define SYSCON_SARADC_SAR2_PATT_TAB1_REG (DR_REG_SYSCON_BASE + 0x2C) +/* SYSCON_SARADC_SAR2_PATT_TAB1 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: item 0 ~ 3 for pattern table 2 (each item one byte)*/ +#define SYSCON_SARADC_SAR2_PATT_TAB1 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB1_M ((SYSCON_SARADC_SAR2_PATT_TAB1_V)<<(SYSCON_SARADC_SAR2_PATT_TAB1_S)) +#define SYSCON_SARADC_SAR2_PATT_TAB1_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB1_S 0 + +#define SYSCON_SARADC_SAR2_PATT_TAB2_REG (DR_REG_SYSCON_BASE + 0x30) +/* SYSCON_SARADC_SAR2_PATT_TAB2 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 4 ~ 7 for pattern table 2 (each item one byte)*/ +#define SYSCON_SARADC_SAR2_PATT_TAB2 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB2_M ((SYSCON_SARADC_SAR2_PATT_TAB2_V)<<(SYSCON_SARADC_SAR2_PATT_TAB2_S)) +#define SYSCON_SARADC_SAR2_PATT_TAB2_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB2_S 0 + +#define SYSCON_SARADC_SAR2_PATT_TAB3_REG (DR_REG_SYSCON_BASE + 0x34) +/* SYSCON_SARADC_SAR2_PATT_TAB3 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 8 ~ 11 for pattern table 2 (each item one byte)*/ +#define SYSCON_SARADC_SAR2_PATT_TAB3 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB3_M ((SYSCON_SARADC_SAR2_PATT_TAB3_V)<<(SYSCON_SARADC_SAR2_PATT_TAB3_S)) +#define SYSCON_SARADC_SAR2_PATT_TAB3_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB3_S 0 + +#define SYSCON_SARADC_SAR2_PATT_TAB4_REG (DR_REG_SYSCON_BASE + 0x38) +/* SYSCON_SARADC_SAR2_PATT_TAB4 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 12 ~ 15 for pattern table 2 (each item one byte)*/ +#define SYSCON_SARADC_SAR2_PATT_TAB4 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB4_M ((SYSCON_SARADC_SAR2_PATT_TAB4_V)<<(SYSCON_SARADC_SAR2_PATT_TAB4_S)) +#define SYSCON_SARADC_SAR2_PATT_TAB4_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB4_S 0 + +#define SYSCON_APLL_TICK_CONF_REG (DR_REG_SYSCON_BASE + 0x3C) +/* SYSCON_APLL_TICK_NUM : R/W ;bitpos:[7:0] ;default: 8'd99 ; */ +/*description: */ +#define SYSCON_APLL_TICK_NUM 0x000000FF +#define SYSCON_APLL_TICK_NUM_M ((SYSCON_APLL_TICK_NUM_V)<<(SYSCON_APLL_TICK_NUM_S)) +#define SYSCON_APLL_TICK_NUM_V 0xFF +#define SYSCON_APLL_TICK_NUM_S 0 + +#define SYSCON_DATE_REG (DR_REG_SYSCON_BASE + 0x7C) +/* SYSCON_DATE : R/W ;bitpos:[31:0] ;default: 32'h16042000 ; */ +/*description: */ +#define SYSCON_DATE 0xFFFFFFFF +#define SYSCON_DATE_M ((SYSCON_DATE_V)<<(SYSCON_DATE_S)) +#define SYSCON_DATE_V 0xFFFFFFFF +#define SYSCON_DATE_S 0 + + + + +#endif /*_SOC_SYSCON_REG_H_ */ + + diff --git a/components/esp32/include/soc/syscon_struct.h b/components/esp32/include/soc/syscon_struct.h new file mode 100644 index 0000000000..700aeecf4c --- /dev/null +++ b/components/esp32/include/soc/syscon_struct.h @@ -0,0 +1,120 @@ +// Copyright 2015-2016 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 _SOC_SYSCON_STRUCT_H_ +#define _SOC_SYSCON_STRUCT_H_ +typedef struct { + union { + struct { + volatile uint32_t pre_div: 10; + volatile uint32_t clk_320m_en: 1; + volatile uint32_t clk_en: 1; + volatile uint32_t rst_tick: 1; + volatile uint32_t quick_clk_chng: 1; + volatile uint32_t reserved14: 18; + }; + volatile uint32_t val; + }clk_conf; + union { + struct { + volatile uint32_t xtal_tick: 8; + volatile uint32_t reserved8: 24; + }; + volatile uint32_t val; + }xtal_tick_conf; + union { + struct { + volatile uint32_t pll_tick: 8; + volatile uint32_t reserved8: 24; + }; + volatile uint32_t val; + }pll_tick_conf; + union { + struct { + volatile uint32_t ck8m_tick: 8; + volatile uint32_t reserved8: 24; + }; + volatile uint32_t val; + }ck8m_tick_conf; + union { + struct { + volatile uint32_t start_force: 1; + volatile uint32_t start: 1; + volatile uint32_t sar2_mux: 1; /*1: SAR ADC2 is controlled by DIG ADC2 CTRL 0: SAR ADC2 is controlled by PWDET CTRL*/ + volatile uint32_t work_mode: 2; /*0: single mode 1: double mode 2: alternate mode*/ + volatile uint32_t sar_sel: 1; /*0: SAR1 1: SAR2 only work for single SAR mode*/ + volatile uint32_t sar_clk_gated: 1; + volatile uint32_t sar_clk_div: 8; /*SAR clock divider*/ + volatile uint32_t sar1_patt_len: 4; /*0 ~ 15 means length 1 ~ 16*/ + volatile uint32_t sar2_patt_len: 4; /*0 ~ 15 means length 1 ~ 16*/ + volatile uint32_t sar1_patt_p_clear: 1; /*clear the pointer of pattern table for DIG ADC1 CTRL*/ + volatile uint32_t sar2_patt_p_clear: 1; /*clear the pointer of pattern table for DIG ADC2 CTRL*/ + volatile uint32_t data_sar_sel: 1; /*1: sar_sel will be coded by the MSB of the 16-bit output data in this case the resolution should not be larger than 11 bits.*/ + volatile uint32_t data_to_i2s: 1; /*1: I2S input data is from SAR ADC (for DMA) 0: I2S input data is from GPIO matrix*/ + volatile uint32_t reserved27: 5; + }; + volatile uint32_t val; + }saradc_ctrl; + union { + struct { + volatile uint32_t meas_num_limit: 1; + volatile uint32_t max_meas_num: 8; /*max conversion number*/ + volatile uint32_t sar1_inv: 1; /*1: data to DIG ADC1 CTRL is inverted otherwise not*/ + volatile uint32_t sar2_inv: 1; /*1: data to DIG ADC2 CTRL is inverted otherwise not*/ + volatile uint32_t reserved11: 21; + }; + volatile uint32_t val; + }saradc_ctrl2; + union { + struct { + volatile uint32_t rstb_wait: 8; + volatile uint32_t standby_wait: 8; + volatile uint32_t start_wait: 8; + volatile uint32_t sample_cycle: 8; /*sample cycles*/ + }; + volatile uint32_t val; + }saradc_fsm; + volatile uint32_t saradc_sar1_patt_tab1; /*item 0 ~ 3 for pattern table 1 (each item one byte)*/ + volatile uint32_t saradc_sar1_patt_tab2; /*Item 4 ~ 7 for pattern table 1 (each item one byte)*/ + volatile uint32_t saradc_sar1_patt_tab3; /*Item 8 ~ 11 for pattern table 1 (each item one byte)*/ + volatile uint32_t saradc_sar1_patt_tab4; /*Item 12 ~ 15 for pattern table 1 (each item one byte)*/ + volatile uint32_t saradc_sar2_patt_tab1; /*item 0 ~ 3 for pattern table 2 (each item one byte)*/ + volatile uint32_t saradc_sar2_patt_tab2; /*Item 4 ~ 7 for pattern table 2 (each item one byte)*/ + volatile uint32_t saradc_sar2_patt_tab3; /*Item 8 ~ 11 for pattern table 2 (each item one byte)*/ + volatile uint32_t saradc_sar2_patt_tab4; /*Item 12 ~ 15 for pattern table 2 (each item one byte)*/ + union { + struct { + volatile uint32_t apll_tick: 8; + volatile uint32_t reserved8: 24; + }; + volatile uint32_t val; + }apll_tick_conf; + volatile uint32_t reserved_40; + volatile uint32_t reserved_44; + volatile uint32_t reserved_48; + volatile uint32_t reserved_4c; + volatile uint32_t reserved_50; + volatile uint32_t reserved_54; + volatile uint32_t reserved_58; + volatile uint32_t reserved_5c; + volatile uint32_t reserved_60; + volatile uint32_t reserved_64; + volatile uint32_t reserved_68; + volatile uint32_t reserved_6c; + volatile uint32_t reserved_70; + volatile uint32_t reserved_74; + volatile uint32_t reserved_78; + volatile uint32_t date; /**/ +} syscon_dev_t; + +#endif /* _SOC_SYSCON_STRUCT_H_ */ diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c index 9476a433d4..1cb2fba5d9 100644 --- a/components/esp32/intr_alloc.c +++ b/components/esp32/intr_alloc.c @@ -446,6 +446,12 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre if ((flags&ESP_INTR_FLAG_SHARED) && (!handler || source<0)) return ESP_ERR_INVALID_ARG; //Statusreg should have a mask if (intrstatusreg && !intrstatusmask) return ESP_ERR_INVALID_ARG; + //If the ISR is marked to be IRAM-resident, the handler must not be in the cached region + if ((flags&ESP_INTR_FLAG_IRAM) && + (ptrdiff_t) handler >= 0x400C0000 && + (ptrdiff_t) handler < 0x50000000 ) { + return ESP_ERR_INVALID_ARG; + } //Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts. if ((flags&ESP_INTR_FLAG_LEVELMASK)==0) { @@ -691,7 +697,7 @@ void esp_intr_noniram_disable() int oldint; int cpu=xPortGetCoreID(); int intmask=~non_iram_int_mask[cpu]; - assert(non_iram_int_disabled_flag[cpu]==false); + if (non_iram_int_disabled_flag[cpu]) abort(); non_iram_int_disabled_flag[cpu]=true; asm volatile ( "movi %0,0\n" @@ -709,7 +715,7 @@ void esp_intr_noniram_enable() { int cpu=xPortGetCoreID(); int intmask=non_iram_int_disabled[cpu]; - assert(non_iram_int_disabled_flag[cpu]==true); + if (!non_iram_int_disabled_flag[cpu]) abort(); non_iram_int_disabled_flag[cpu]=false; asm volatile ( "movi a3,0\n" diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index aafafbb495..8cc3b5b22e 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -19,9 +19,11 @@ SECTIONS */ .rtc.data : { + _rtc_data_start = ABSOLUTE(.); *(.rtc.data) *(.rtc.rodata) *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + _rtc_data_end = ABSOLUTE(.); } > rtc_slow_seg /* RTC bss, from any source file named rtc_wake_stub*.c */ @@ -85,29 +87,6 @@ SECTIONS _iram_text_end = ABSOLUTE(.); } > iram0_0_seg - /* Shared RAM */ - .dram0.bss (NOLOAD) : - { - . = ALIGN (8); - _bss_start = ABSOLUTE(.); - *(.dynsbss) - *(.sbss) - *(.sbss.*) - *(.gnu.linkonce.sb.*) - *(.scommon) - *(.sbss2) - *(.sbss2.*) - *(.gnu.linkonce.sb2.*) - *(.dynbss) - KEEP(*(.bss)) - *(.bss.*) - *(.share.mem) - *(.gnu.linkonce.b.*) - *(COMMON) - . = ALIGN (8); - _bss_end = ABSOLUTE(.); - } >dram0_0_seg - .dram0.data : { _data_start = ABSOLUTE(.); @@ -126,6 +105,29 @@ SECTIONS *libesp32.a:panic.o(.rodata .rodata.*) _data_end = ABSOLUTE(.); . = ALIGN(4); + } >dram0_0_seg + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.share.mem) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); _heap_start = ABSOLUTE(.); } >dram0_0_seg diff --git a/components/esp32/ld/esp32.peripherals.ld b/components/esp32/ld/esp32.peripherals.ld index 95aaadcbcc..8d8d4e8b1b 100644 --- a/components/esp32/ld/esp32.peripherals.ld +++ b/components/esp32/ld/esp32.peripherals.ld @@ -19,3 +19,4 @@ PROVIDE ( SPI3 = 0x3ff65000 ); PROVIDE ( I2C1 = 0x3ff67000 ); PROVIDE ( I2S1 = 0x3ff6D000 ); PROVIDE ( UART2 = 0x3ff6E000 ); +PROVIDE ( SDMMC = 0x3ff68000 ); diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index 6241ff840e..7543fa42aa 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -87,9 +87,9 @@ PROVIDE ( _ctype_ = 0x3ff96354 ); PROVIDE ( __ctype_ptr__ = 0x3ff96350 ); PROVIDE ( __ctzdi2 = 0x4000ca64 ); PROVIDE ( __ctzsi2 = 0x4000c7f0 ); -PROVIDE ( _data_end = 0x4000d5c8 ); +PROVIDE ( _data_end_rom = 0x4000d5c8 ); PROVIDE ( _data_end_btdm_rom = 0x4000d4f8 ); -PROVIDE ( _data_start = 0x4000d4f8 ); +PROVIDE ( _data_start_rom = 0x4000d4f8 ); PROVIDE ( _data_start_btdm_rom = 0x4000d4f4 ); PROVIDE ( _data_start_btdm = 0x3ffae6e0); PROVIDE ( _data_end_btdm = 0x3ffaff10); @@ -202,7 +202,7 @@ PROVIDE ( ets_timer_init = 0x400084e8 ); PROVIDE ( ets_timer_setfn = 0x40008350 ); PROVIDE ( ets_unpack_flash_code = 0x40007018 ); PROVIDE ( ets_unpack_flash_code_legacy = 0x4000694c ); -PROVIDE ( ets_update_cpu_frequency = 0x40008550 ); +PROVIDE ( ets_update_cpu_frequency_rom = 0x40008550 ); /* Updates g_ticks_per_us on the current CPU only; not on the other core */ PROVIDE ( ets_waiti0 = 0x400067d8 ); PROVIDE ( exc_cause_table = 0x3ff991d0 ); PROVIDE ( _exit_r = 0x4000bd28 ); @@ -1725,6 +1725,8 @@ PROVIDE ( xthal_memcpy = 0x4000c0bc ); PROVIDE ( xthal_set_ccompare = 0x4000c058 ); PROVIDE ( xthal_set_intclear = 0x4000c1ec ); PROVIDE ( _xtos_set_intlevel = 0x4000bfdc ); +PROVIDE ( g_ticks_per_us_pro = 0x3ffe01e0 ); +PROVIDE ( g_ticks_per_us_app = 0x3ffe40f0 ); /* These functions are xtos-related (or call xtos-related functions) and do not play well with multicore FreeRTOS. Where needed, we provide alternatives that are multicore diff --git a/components/esp32/lib b/components/esp32/lib index 02232f974b..b26cd21764 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 02232f974b0ff1568ddd6d7015a41fb4f4870994 +Subproject commit b26cd217641acf08da932f4de8e858083ea83113 diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 0efe56fe01..6180770bd6 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -32,11 +32,13 @@ #include "esp_gdbstub.h" #include "esp_panic.h" #include "esp_attr.h" +#include "esp_err.h" +#include "esp_core_dump.h" /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level task switching / interrupt code runs into an unrecoverable error. The default task stack - overflow handler also is in here. + overflow handler and abort handler are also in here. */ /* @@ -45,13 +47,13 @@ #if !CONFIG_ESP32_PANIC_SILENT_REBOOT //printf may be broken, so we fix our own printing fns... -inline static void panicPutChar(char c) +static void panicPutChar(char c) { while (((READ_PERI_REG(UART_STATUS_REG(0)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ; WRITE_PERI_REG(UART_FIFO_REG(0), c); } -inline static void panicPutStr(const char *c) +static void panicPutStr(const char *c) { int x = 0; while (c[x] != 0) { @@ -60,7 +62,7 @@ inline static void panicPutStr(const char *c) } } -inline static void panicPutHex(int a) +static void panicPutHex(int a) { int x; int c; @@ -75,7 +77,7 @@ inline static void panicPutHex(int a) } } -inline static void panicPutDec(int a) +static void panicPutDec(int a) { int n1, n2; n1 = a % 10; @@ -89,21 +91,35 @@ inline static void panicPutDec(int a) } #else //No printing wanted. Stub out these functions. -inline static void panicPutChar(char c) { } -inline static void panicPutStr(const char *c) { } -inline static void panicPutHex(int a) { } -inline static void panicPutDec(int a) { } +static void panicPutChar(char c) { } +static void panicPutStr(const char *c) { } +static void panicPutHex(int a) { } +static void panicPutDec(int a) { } #endif - void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) { panicPutStr("***ERROR*** A stack overflow in task "); panicPutStr((char *)pcTaskName); panicPutStr(" has been detected.\r\n"); - configASSERT(0); + abort(); } +static bool abort_called; + +void abort() +{ +#if !CONFIG_ESP32_PANIC_SILENT_REBOOT + ets_printf("abort() was called at PC 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3); +#endif + abort_called = true; + while(1) { + __asm__ ("break 0,0"); + *((int*) 0) = 0; + } +} + + static const char *edesc[] = { "IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError", "Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue", @@ -118,7 +134,7 @@ static const char *edesc[] = { }; -void commonErrorHandler(XtExcFrame *frame); +static void commonErrorHandler(XtExcFrame *frame); //The fact that we've panic'ed probably means the other CPU is now running wild, possibly //messing up the serial output, so we stall it here. @@ -127,19 +143,6 @@ static void haltOtherCore() esp_cpu_stall( xPortGetCoreID() == 0 ? 1 : 0 ); } -//Returns true when a debugger is attached using JTAG. -static int inOCDMode() -{ -#if CONFIG_ESP32_DEBUG_OCDAWARE - int dcr; - int reg = 0x10200C; //DSRSET register - asm("rer %0,%1":"=r"(dcr):"r"(reg)); - return (dcr & 0x1); -#else - return 0; //Always return no debugger is attached. -#endif -} - void panicHandler(XtExcFrame *frame) { int *regs = (int *)frame; @@ -162,10 +165,39 @@ void panicHandler(XtExcFrame *frame) panicPutStr("Guru Meditation Error: Core "); panicPutDec(xPortGetCoreID()); panicPutStr(" panic'ed ("); - panicPutStr(reason); - panicPutStr(")\r\n"); + if (!abort_called) { + panicPutStr(reason); + panicPutStr(")\r\n"); + if (regs[20]==PANIC_RSN_DEBUGEXCEPTION) { + int debugRsn; + asm("rsr.debugcause %0":"=r"(debugRsn)); + panicPutStr("Debug exception reason: "); + if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) panicPutStr("SingleStep "); + if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) panicPutStr("HwBreakpoint "); + if (debugRsn&XCHAL_DEBUGCAUSE_DBREAK_MASK) { + //Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK + //reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the + //debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0. + if (debugRsn&(1<<8)) { +#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK + panicPutStr("Stack canary watchpoint triggered "); +#else + panicPutStr("Watchpoint 1 triggered "); +#endif + } else { + panicPutStr("Watchpoint 0 triggered "); + } + } + if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) panicPutStr("BREAK instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) panicPutStr("BREAKN instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) panicPutStr("DebugIntr "); + panicPutStr("\r\n"); + } + } else { + panicPutStr("abort)\r\n"); + } - if (inOCDMode()) { + if (esp_cpu_in_ocd_debug_mode()) { asm("break.n 1"); } commonErrorHandler(frame); @@ -197,7 +229,7 @@ void xt_unhandled_exception(XtExcFrame *frame) } panicPutStr(" occurred on core "); panicPutDec(xPortGetCoreID()); - if (inOCDMode()) { + if (esp_cpu_in_ocd_debug_mode()) { panicPutStr(" at pc="); panicPutHex(regs[1]); panicPutStr(". Setting bp and returning..\r\n"); @@ -255,6 +287,7 @@ static inline bool stackPointerIsSane(uint32_t sp) { return !(sp < 0x3ffae010 || sp > 0x3ffffff0 || ((sp & 0xf) != 0)); } + static void putEntry(uint32_t pc, uint32_t sp) { if (pc & 0x80000000) { @@ -265,10 +298,11 @@ static void putEntry(uint32_t pc, uint32_t sp) panicPutStr(":0x"); panicPutHex(sp); } -void doBacktrace(XtExcFrame *frame) + +static void doBacktrace(XtExcFrame *frame) { uint32_t i = 0, pc = frame->pc, sp = frame->a1; - panicPutStr("\nBacktrace:"); + panicPutStr("\r\nBacktrace:"); /* Do not check sanity on first entry, PC could be smashed. */ putEntry(pc, sp); pc = frame->a0; @@ -284,14 +318,16 @@ void doBacktrace(XtExcFrame *frame) break; } } - panicPutStr("\n\n"); + panicPutStr("\r\n\r\n"); } +void esp_restart_noos() __attribute__ ((noreturn)); + /* We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the serial port and either jump to the gdb stub, halt the CPU or reboot. */ -void commonErrorHandler(XtExcFrame *frame) +static void commonErrorHandler(XtExcFrame *frame) { int *regs = (int *)frame; int x, y; @@ -304,43 +340,101 @@ void commonErrorHandler(XtExcFrame *frame) //Feed the watchdogs, so they will give us time to print out debug info reconfigureAllWdts(); - panicPutStr("Register dump:\r\n"); + /* only dump registers for 'real' crashes, if crashing via abort() + the register window is no longer useful. + */ + if (!abort_called) { + panicPutStr("Register dump:\r\n"); - for (x = 0; x < 24; x += 4) { - for (y = 0; y < 4; y++) { - if (sdesc[x + y][0] != 0) { - panicPutStr(sdesc[x + y]); - panicPutStr(": 0x"); - panicPutHex(regs[x + y + 1]); - panicPutStr(" "); + for (x = 0; x < 24; x += 4) { + for (y = 0; y < 4; y++) { + if (sdesc[x + y][0] != 0) { + panicPutStr(sdesc[x + y]); + panicPutStr(": 0x"); + panicPutHex(regs[x + y + 1]); + panicPutStr(" "); + } } + panicPutStr("\r\n"); } - panicPutStr("\r\n"); } + /* With windowed ABI backtracing is easy, let's do it. */ doBacktrace(frame); + #if CONFIG_ESP32_PANIC_GDBSTUB disableAllWdts(); panicPutStr("Entering gdb stub now.\r\n"); esp_gdbstub_panic_handler(frame); -#elif CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT +#else +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + esp_core_dump_to_flash(frame); +#endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART && !CONFIG_ESP32_PANIC_SILENT_REBOOT + esp_core_dump_to_uart(frame); +#endif +#if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT panicPutStr("Rebooting...\r\n"); - for (x = 0; x < 100; x++) { - ets_delay_us(1000); - } - software_reset(); + esp_restart_noos(); #else disableAllWdts(); panicPutStr("CPU halted.\r\n"); while (1); #endif +#endif } void esp_set_breakpoint_if_jtag(void *fn) { - if (!inOCDMode()) { - return; + if (esp_cpu_in_ocd_debug_mode()) { + setFirstBreakpoint((uint32_t)fn); } - setFirstBreakpoint((uint32_t)fn); } + + +esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags) +{ + int x; + if (no<0 || no>1) return ESP_ERR_INVALID_ARG; + if (flags&(~0xC0000000)) return ESP_ERR_INVALID_ARG; + int dbreakc=0x3F; + //We support watching 2^n byte values, from 1 to 64. Calculate the mask for that. + for (x=0; x<7; x++) { + if (size==(1< +#include +#include +#include +#include "unity.h" +#include "rom/ets_sys.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +typedef struct { + int delay_us; + int method; +} delay_test_arg_t; + +static void test_delay_task(void* p) +{ + const delay_test_arg_t* arg = (delay_test_arg_t*) p; + struct timeval tv_start, tv_stop; + gettimeofday(&tv_start, NULL); + switch (arg->method) { + case 0: + ets_delay_us(arg->delay_us); + break; + case 1: + vTaskDelay(arg->delay_us / portTICK_PERIOD_MS / 1000); + break; + default: + TEST_FAIL(); + } + gettimeofday(&tv_stop, NULL); + int real_delay_us = (tv_stop.tv_sec - tv_start.tv_sec) * 1000000 + + tv_stop.tv_usec - tv_start.tv_usec; + printf("%s core=%d expected=%d actual=%d\n", arg->method ? "vTaskDelay" : "ets_delay_us", + xPortGetCoreID(), arg->delay_us, real_delay_us); + TEST_ASSERT_TRUE(abs(real_delay_us - arg->delay_us) < 1000); + vTaskDelay(1); + vTaskDelete(NULL); +} + +TEST_CASE("ets_delay produces correct delay on both CPUs", "[delay]") +{ + int delay_ms = 50; + const delay_test_arg_t args = { .delay_us = delay_ms * 1000, .method = 0 }; + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 0); + vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 1); + vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); +} + +TEST_CASE("vTaskDelay produces correct delay on both CPUs", "[delay]") +{ + int delay_ms = 50; + const delay_test_arg_t args = { .delay_us = delay_ms * 1000, .method = 1 }; + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 0); + vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 1); + vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); +} diff --git a/components/esp32/test/test_intr_alloc.c b/components/esp32/test/test_intr_alloc.c index 31991b4e41..a5e6f06834 100644 --- a/components/esp32/test/test_intr_alloc.c +++ b/components/esp32/test/test_intr_alloc.c @@ -98,7 +98,7 @@ static void timer_test(int flags) { esp_intr_get_intno(inth[0]), esp_intr_get_intno(inth[1]), esp_intr_get_intno(inth[2]), esp_intr_get_intno(inth[3])); printf("Timer values on start: %d %d %d %d\n", count[0], count[1], count[2], count[3]); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); TEST_ASSERT(count[0]==0); TEST_ASSERT(count[1]!=0); @@ -110,7 +110,7 @@ static void timer_test(int flags) { esp_intr_disable(inth[1]); esp_intr_disable(inth[2]); for (x=0; x<4; x++) count[x]=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); TEST_ASSERT(count[0]!=0); TEST_ASSERT(count[1]==0); @@ -122,7 +122,7 @@ static void timer_test(int flags) { esp_intr_disable(inth[0]); esp_intr_disable(inth[3]); for (x=0; x<4; x++) count[x]=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); TEST_ASSERT(count[0]==0); TEST_ASSERT(count[1]!=0); @@ -152,18 +152,18 @@ void local_timer_test() printf("Int timer 1 intno %d\n", esp_intr_get_intno(ih)); xthal_set_ccompare(1, xthal_get_ccount()+8000000); int_timer_ctr=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr!=0); printf("Disabling int\n"); esp_intr_disable(ih); int_timer_ctr=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr==0); printf("Re-enabling\n"); esp_intr_enable(ih); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr!=0); @@ -173,12 +173,12 @@ void local_timer_test() r=esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED, int_timer_handler, NULL, &ih); TEST_ASSERT(r==ESP_OK); int_timer_ctr=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr==0); printf("Re-enabling\n"); esp_intr_enable(ih); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr!=0); r=esp_intr_free(ih); @@ -201,3 +201,30 @@ TEST_CASE("Intr_alloc test, shared ints", "[esp32]") { timer_test(ESP_INTR_FLAG_SHARED); } + +TEST_CASE("Can allocate IRAM int only with an IRAM handler", "[esp32]") +{ + void dummy(void* arg) + { + } + IRAM_ATTR void dummy_iram(void* arg) + { + } + RTC_IRAM_ATTR void dummy_rtc(void* arg) + { + } + intr_handle_t ih; + esp_err_t err = esp_intr_alloc(ETS_INTERNAL_PROFILING_INTR_SOURCE, + ESP_INTR_FLAG_IRAM, &dummy, NULL, &ih); + TEST_ASSERT_EQUAL_INT(ESP_ERR_INVALID_ARG, err); + err = esp_intr_alloc(ETS_INTERNAL_PROFILING_INTR_SOURCE, + ESP_INTR_FLAG_IRAM, &dummy_iram, NULL, &ih); + TEST_ESP_OK(err); + err = esp_intr_free(ih); + TEST_ESP_OK(err); + err = esp_intr_alloc(ETS_INTERNAL_PROFILING_INTR_SOURCE, + ESP_INTR_FLAG_IRAM, &dummy_rtc, NULL, &ih); + TEST_ESP_OK(err); + err = esp_intr_free(ih); + TEST_ESP_OK(err); +} diff --git a/components/esp_audio/component.mk b/components/esp_audio/component.mk deleted file mode 100644 index f232add3d8..0000000000 --- a/components/esp_audio/component.mk +++ /dev/null @@ -1,29 +0,0 @@ -# -# Component Makefile -# -# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, -# this will take the sources in this directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the esp-idf build system document if you need to do this. -# -LIBS := esp_audio - -COMPONENT_ADD_INCLUDEDIRS := include include/driver - -COMPONENT_ADD_LDFLAGS := \ - $(abspath lib/libEspAudio.a) \ - $(abspath lib/libMisc.a) \ - $(abspath lib/libMediaHal.a) \ - $(abspath lib/libdlna.a) \ - -include $(IDF_PATH)/make/component_common.mk - - -# this is a hack to make sure the app is re-linked if the binary -# libraries change or are updated. If they change, the main esp32 -# library will be rebuild by AR andthis will trigger a re-linking of -# the entire app. -# -# It would be better for components to be able to expose any of these -# non-standard dependencies via get_variable, but this will do for now. -$(COMPONENT_LIBRARY): $(ALL_LIB_FILES) diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py new file mode 100755 index 0000000000..1589a362b9 --- /dev/null +++ b/components/espcoredump/espcoredump.py @@ -0,0 +1,1109 @@ +#!/usr/bin/env python +# +# ESP32 core dump Utility + +import sys +import os +import argparse +import subprocess +import tempfile +import struct +import array +import errno +import base64 + +try: + import esptool +except ImportError: + idf_path = os.getenv('IDF_PATH') + if idf_path is None: + print "Esptool is not found! Install it or set proper $IDF_PATH in environment." + sys.exit(2) + sys.path.append('%s/components/esptool_py/esptool' % idf_path) + import esptool + +__version__ = "0.1-dev" + +if os.name == 'nt': + CLOSE_FDS = False +else: + CLOSE_FDS = True + + +class ESPCoreDumpError(RuntimeError): + """Core dump runtime error class + """ + def __init__(self, message): + """Constructor for core dump error + """ + super(ESPCoreDumpError, self).__init__(message) + +class BinStruct(object): + """Binary structure representation + + Subclasses must specify actual structure layout using 'fields' and 'format' members. + For example, the following subclass represents structure with two fields: + f1 of size 2 bytes and 4 bytes f2. Little endian. + class SomeStruct(BinStruct): + fields = ("f1", + "f2") + format = " 0: + self._read_sections(f, shoff, shstrndx) + else: + self.sections = [] + if phnum > 0: + self._read_program_segments(f, phoff, phentsize, phnum) + else: + self.program_segments = [] + + def _read_sections(self, f, section_header_offs, shstrndx): + """Reads core dump sections from ELF file + """ + f.seek(section_header_offs) + section_header = f.read() + LEN_SEC_HEADER = 0x28 + if len(section_header) == 0: + raise ESPCoreDumpError("No section header found at offset %04x in ELF file." % section_header_offs) + if len(section_header) % LEN_SEC_HEADER != 0: + print 'WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER) + + # walk through the section header and extract all sections + section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER) + + def read_section_header(offs): + name_offs,sec_type,flags,lma,sec_offs,size = struct.unpack_from("= ps.addr and addr < (ps.addr + seg_len): + raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) + if (addr + data_sz) > ps.addr and (addr + data_sz) <= (ps.addr + seg_len): + raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) + # append + self.program_segments.append(ESPCoreDumpSegment(addr, data, type, flags)) + + def dump(self, f): + """Write core dump contents to file + """ + # TODO: currently dumps only program segments. + # dumping sections is not supported yet + # write ELF header + ehdr = Elf32FileHeader() + ehdr.e_type = self.e_type + ehdr.e_machine = self.e_machine + ehdr.e_entry = 0 + ehdr.e_phoff = ehdr.sizeof() + ehdr.e_shoff = 0 + ehdr.e_flags = 0 + ehdr.e_phentsize = Elf32ProgramHeader().sizeof() + ehdr.e_phnum = len(self.program_segments) + ehdr.e_shentsize = 0 + ehdr.e_shnum = 0 + ehdr.e_shstrndx = self.SHN_UNDEF + f.write(ehdr.dump()) + # write program header table + cur_off = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize + for i in range(len(self.program_segments)): + phdr = Elf32ProgramHeader() + phdr.p_type = self.program_segments[i].type + phdr.p_offset = cur_off + phdr.p_vaddr = self.program_segments[i].addr + phdr.p_paddr = phdr.p_vaddr # TODO + phdr.p_filesz = len(self.program_segments[i].data) + phdr.p_memsz = phdr.p_filesz # TODO + phdr.p_flags = self.program_segments[i].flags + phdr.p_align = 0 # TODO + f.write(phdr.dump()) + cur_off += phdr.p_filesz + # write program segments + for i in range(len(self.program_segments)): + f.write(self.program_segments[i].data) + + +class ESPCoreDumpLoaderError(ESPCoreDumpError): + """Core dump loader error class + """ + def __init__(self, message): + """Constructor for core dump loader error + """ + super(ESPCoreDumpLoaderError, self).__init__(message) + + +class ESPCoreDumpLoader(object): + """Core dump loader base class + """ + ESP32_COREDUMP_HDR_FMT = '<3L' + ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) + ESP32_COREDUMP_TSK_HDR_FMT = '<3L' + ESP32_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP32_COREDUMP_TSK_HDR_FMT) + + def __init__(self): + """Base constructor for core dump loader + """ + self.fcore = None + + def _get_registers_from_stack(self, data, grows_down): + """Returns list of registers (in GDB format) from xtensa stack frame + """ + # from "gdb/xtensa-tdep.h" + # typedef struct + # { + #0 xtensa_elf_greg_t pc; + #1 xtensa_elf_greg_t ps; + #2 xtensa_elf_greg_t lbeg; + #3 xtensa_elf_greg_t lend; + #4 xtensa_elf_greg_t lcount; + #5 xtensa_elf_greg_t sar; + #6 xtensa_elf_greg_t windowstart; + #7 xtensa_elf_greg_t windowbase; + #8..63 xtensa_elf_greg_t reserved[8+48]; + #64 xtensa_elf_greg_t ar[64]; + # } xtensa_elf_gregset_t; + REG_PC_IDX=0 + REG_PS_IDX=1 + REG_LB_IDX=2 + REG_LE_IDX=3 + REG_LC_IDX=4 + REG_SAR_IDX=5 + REG_WS_IDX=6 + REG_WB_IDX=7 + REG_AR_START_IDX=64 + REG_AR_NUM=64 + # FIXME: acc to xtensa_elf_gregset_t number of regs must be 128, + # but gdb complanis when it less then 129 + REG_NUM=129 + + XT_SOL_EXIT=0 + XT_SOL_PC=1 + XT_SOL_PS=2 + XT_SOL_NEXT=3 + XT_SOL_AR_START=4 + XT_SOL_AR_NUM=4 + XT_SOL_FRMSZ=8 + + XT_STK_EXIT=0 + XT_STK_PC=1 + XT_STK_PS=2 + XT_STK_AR_START=3 + XT_STK_AR_NUM=16 + XT_STK_SAR=19 + XT_STK_EXCCAUSE=20 + XT_STK_EXCVADDR=21 + XT_STK_LBEG=22 + XT_STK_LEND=23 + XT_STK_LCOUNT=24 + XT_STK_FRMSZ=25 + + regs = [0] * REG_NUM + # TODO: support for growing up stacks + if not grows_down: + raise ESPCoreDumpLoaderError("Growing up stacks are not supported for now!") + ex_struct = "<%dL" % XT_STK_FRMSZ + if len(data) < struct.calcsize(ex_struct): + raise ESPCoreDumpLoaderError("Too small stack to keep frame: %d bytes!" % len(data)) + + stack = struct.unpack(ex_struct, data[:struct.calcsize(ex_struct)]) + # Stack frame type indicator is always the first item + rc = stack[XT_STK_EXIT] + if rc != 0: + regs[REG_PC_IDX] = stack[XT_STK_PC] + regs[REG_PS_IDX] = stack[XT_STK_PS] + for i in range(XT_STK_AR_NUM): + regs[REG_AR_START_IDX + i] = stack[XT_STK_AR_START + i] + regs[REG_SAR_IDX] = stack[XT_STK_SAR] + regs[REG_LB_IDX] = stack[XT_STK_LBEG] + regs[REG_LE_IDX] = stack[XT_STK_LEND] + regs[REG_LC_IDX] = stack[XT_STK_LCOUNT] + # FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set + # and GDB can not unwind callstack properly (it implies not windowed call0) + if regs[REG_PS_IDX] & (1 << 5): + regs[REG_PS_IDX] &= ~(1 << 4) + else: + regs[REG_PC_IDX] = stack[XT_SOL_PC] + regs[REG_PS_IDX] = stack[XT_SOL_PS] + for i in range(XT_SOL_AR_NUM): + regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i] + nxt = stack[XT_SOL_NEXT] + + # TODO: remove magic hack with saved PC to get proper value + regs[REG_PC_IDX] = ((regs[REG_PC_IDX] & 0x3FFFFFFF) | 0x40000000) + if regs[REG_PC_IDX] & 0x80000000: + regs[REG_PC_IDX] = (regs[REG_PC_IDX] & 0x3fffffff) | 0x40000000; + if regs[REG_AR_START_IDX + 0] & 0x80000000: + regs[REG_AR_START_IDX + 0] = (regs[REG_AR_START_IDX + 0] & 0x3fffffff) | 0x40000000; + return regs + + def remove_tmp_file(self, fname): + """Silently removes temporary file + """ + try: + os.remove(fname) + except OSError as e: + if e.errno != errno.ENOENT: + print "Warning failed to remove temp file '%s' (%d)!" % (fname, e.errno) + + def cleanup(self): + """Cleans up loader resources + """ + if self.fcore: + self.fcore.close() + if self.fcore_name: + self.remove_tmp_file(self.fcore_name) + + def create_corefile(self, core_fname=None, off=0): + """Creates core dump ELF file + """ + core_off = off + data = self.read_data(core_off, self.ESP32_COREDUMP_HDR_SZ) + tot_len,task_num,tcbsz = struct.unpack_from(self.ESP32_COREDUMP_HDR_FMT, data) + tcbsz_aligned = tcbsz + if tcbsz_aligned % 4: + tcbsz_aligned = 4*(tcbsz_aligned/4 + 1) + core_off += self.ESP32_COREDUMP_HDR_SZ + core_elf = ESPCoreDumpElfFile() + notes = b'' + for i in range(task_num): + data = self.read_data(core_off, self.ESP32_COREDUMP_TSK_HDR_SZ) + tcb_addr,stack_top,stack_end = struct.unpack_from(self.ESP32_COREDUMP_TSK_HDR_FMT, data) + if stack_end > stack_top: + stack_len = stack_end - stack_top + stack_base = stack_top + else: + stack_len = stack_top - stack_end + stack_base = stack_end + + stack_len_aligned = stack_len + if stack_len_aligned % 4: + stack_len_aligned = 4*(stack_len_aligned/4 + 1) + + core_off += self.ESP32_COREDUMP_TSK_HDR_SZ + data = self.read_data(core_off, tcbsz_aligned) + if tcbsz != tcbsz_aligned: + core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + else: + core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_off += tcbsz_aligned + data = self.read_data(core_off, stack_len_aligned) + if stack_len != stack_len_aligned: + data = data[:stack_len - stack_len_aligned] + core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_off += stack_len_aligned + try: + task_regs = self._get_registers_from_stack(data, stack_end > stack_top) + except Exception as e: + print e + return None + prstatus = XtensaPrStatus() + prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task + prstatus.pr_pid = i # TODO: use pid assigned by OS + note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump() + notes += note + + # add notes + core_elf.add_program_segment(0, notes, ESPCoreDumpElfFile.PT_NOTE, 0) + + core_elf.e_type = ESPCoreDumpElfFile.ET_CORE + core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA + if core_fname: + fce = open(core_fname, 'wb') + else: + fhnd,core_fname = tempfile.mkstemp() + fce = os.fdopen(fhnd, 'wb') + core_elf.dump(fce) + fce.close() + return core_fname + + def read_data(self, off, sz): + """Reads data from raw core dump got from flash or UART + """ + self.fcore.seek(off) + data = self.fcore.read(sz) + return data + + +class ESPCoreDumpFileLoader(ESPCoreDumpLoader): + """Core dump file loader class + """ + def __init__(self, path, b64 = False): + """Constructor for core dump file loader + """ + super(ESPCoreDumpFileLoader, self).__init__() + self.fcore = self._load_coredump(path, b64) + + def _load_coredump(self, path, b64): + """Loads core dump from (raw binary or base64-encoded) file + """ + self.fcore_name = None + if b64: + fhnd,self.fcore_name = tempfile.mkstemp() + fcore = os.fdopen(fhnd, 'wb') + fb64 = open(path, 'rb') + try: + while True: + line = fb64.readline() + if len(line) == 0: + break + data = base64.standard_b64decode(line.rstrip('\r\n')) + fcore.write(data) + fcore.close() + fcore = open(self.fcore_name, 'rb') + except Exception as e: + if self.fcore_name: + self.remove_tmp_file(self.fcore_name) + raise e + finally: + fb64.close() + else: + fcore = open(path, 'rb') + return fcore + + +class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): + """Core dump flash loader class + """ + ESP32_COREDUMP_FLASH_MAGIC_START = 0xE32C04ED + ESP32_COREDUMP_FLASH_MAGIC_END = 0xE32C04ED + ESP32_COREDUMP_FLASH_MAGIC_FMT = ' sl: + self.result_str = ln[sl:] + if self.result_str.startswith(','): + self.result_str = self.result_str[1:] + else: + print "Invalid result format: '%s'" % ln + else: + self.result_str = '' + return True + return False + + def execute(self, ln): + """Executes GDB/MI result handler function + """ + GDBMIOutRecordHandler.execute(self, ln) + if self._parse_rc(ln, self.RC_DONE): + return + if self._parse_rc(ln, self.RC_RUNNING): + return + if self._parse_rc(ln, self.RC_CONNECTED): + return + if self._parse_rc(ln, self.RC_ERROR): + return + if self._parse_rc(ln, self.RC_EXIT): + return + print "Unknown GDB/MI result: '%s'" % ln + + +class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler): + """GDB/MI console stream handler class + """ + TAG = '~' + + +def dbg_corefile(args): + """ Command to load core dump from file or flash and run GDB debug session with it + """ + global CLOSE_FDS + loader = None + if not args.core: + loader = ESPCoreDumpFlashLoader(args.off, port=args.port) + core_fname = loader.create_corefile(args.save_core) + if not core_fname: + print "Failed to create corefile!" + loader.cleanup() + return + else: + core_fname = args.core + if args.core_format and args.core_format != 'elf': + loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64') + core_fname = loader.create_corefile(args.save_core) + if not core_fname: + print "Failed to create corefile!" + loader.cleanup() + return + + p = subprocess.Popen( + bufsize = 0, + args = [args.gdb, + '--nw', # ignore .gdbinit + '--core=%s' % core_fname, # core file + args.prog], + stdin = None, stdout = None, stderr = None, + close_fds = CLOSE_FDS + ) + p.wait() + + if loader: + if not args.core and not args.save_core: + loader.remove_tmp_file(core_fname) + loader.cleanup() + print 'Done!' + + +def info_corefile(args): + """ Command to load core dump from file or flash and print it's data in user friendly form + """ + global CLOSE_FDS + def gdbmi_console_stream_handler(ln): + sys.stdout.write(ln) + sys.stdout.flush() + + def gdbmi_read2prompt(f, out_handlers=None): + while True: + ln = f.readline().rstrip(' \r\n') + if ln == '(gdb)': + break + elif len(ln) == 0: + break + elif out_handlers: + for h in out_handlers: + if ln.startswith(out_handlers[h].TAG): + out_handlers[h].execute(ln) + break + + loader = None + if not args.core: + loader = ESPCoreDumpFlashLoader(args.off, port=args.port) + core_fname = loader.create_corefile(args.save_core) + if not core_fname: + print "Failed to create corefile!" + loader.cleanup() + return + else: + core_fname = args.core + if args.core_format and args.core_format != 'elf': + loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64') + core_fname = loader.create_corefile(args.save_core) + if not core_fname: + print "Failed to create corefile!" + loader.cleanup() + return + + handlers = {} + handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False) + handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) + p = subprocess.Popen( + bufsize = 0, + args = [args.gdb, + '--quiet', # inhibit dumping info at start-up + '--nx', # inhibit window interface + '--nw', # ignore .gdbinit + '--interpreter=mi2', # use GDB/MI v2 + '--core=%s' % core_fname, # core file + args.prog], + stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, + close_fds = CLOSE_FDS + ) + gdbmi_read2prompt(p.stdout, handlers) + exe_elf = ESPCoreDumpElfFile(args.prog) + core_elf = ESPCoreDumpElfFile(core_fname) + merged_segs = [] + core_segs = core_elf.program_segments + for s in exe_elf.sections: + merged = False + for ps in core_segs: + if ps.addr <= s.addr and ps.addr + len(ps.data) >= s.addr: + # sec: |XXXXXXXXXX| + # seg: |...XXX.............| + seg_addr = ps.addr + if ps.addr + len(ps.data) <= s.addr + len(s.data): + # sec: |XXXXXXXXXX| + # seg: |XXXXXXXXXXX...| + # merged: |XXXXXXXXXXXXXX| + seg_len = len(s.data) + (s.addr - ps.addr) + else: + # sec: |XXXXXXXXXX| + # seg: |XXXXXXXXXXXXXXXXX| + # merged: |XXXXXXXXXXXXXXXXX| + seg_len = len(ps.data) + merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + core_segs.remove(ps) + merged = True + elif ps.addr >= s.addr and ps.addr <= s.addr + len(s.data): + # sec: |XXXXXXXXXX| + # seg: |...XXX.............| + seg_addr = s.addr + if (ps.addr + len(ps.data)) >= (s.addr + len(s.data)): + # sec: |XXXXXXXXXX| + # seg: |..XXXXXXXXXXX| + # merged: |XXXXXXXXXXXXX| + seg_len = len(s.data) + (ps.addr + len(ps.data)) - (s.addr + len(s.data)) + else: + # sec: |XXXXXXXXXX| + # seg: |XXXXXX| + # merged: |XXXXXXXXXX| + seg_len = len(s.data) + merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + core_segs.remove(ps) + merged = True + + if not merged: + merged_segs.append((s.name, s.addr, len(s.data), s.attr_str(), False)) + + print "===============================================================" + print "==================== ESP32 CORE DUMP START ====================" + + handlers[GDBMIResultHandler.TAG].result_class = None + handlers[GDBMIStreamConsoleHandler.TAG].func = gdbmi_console_stream_handler + print "\n================== CURRENT THREAD REGISTERS ===================" + p.stdin.write("-interpreter-exec console \"info registers\"\n") + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + print "\n==================== CURRENT THREAD STACK =====================" + p.stdin.write("-interpreter-exec console \"bt\"\n") + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + print "\n======================== THREADS INFO =========================" + p.stdin.write("-interpreter-exec console \"info threads\"\n") + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + print "\n======================= ALL MEMORY REGIONS ========================" + print "Name Address Size Attrs" + for ms in merged_segs: + print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) + for cs in core_segs: + print ".coredump.tasks 0x%x 0x%x %s" % (cs.addr, len(cs.data), cs.attr_str()) + if args.print_mem: + print "\n====================== CORE DUMP MEMORY CONTENTS ========================" + for cs in core_elf.program_segments: + print ".coredump.tasks 0x%x 0x%x %s" % (cs.addr, len(cs.data), cs.attr_str()) + p.stdin.write("-interpreter-exec console \"x/%dx 0x%x\"\n" % (len(cs.data)/4, cs.addr)) + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + + print "\n===================== ESP32 CORE DUMP END =====================" + print "===============================================================" + + p.stdin.write('q\n') + p.wait() + p.stdin.close() + p.stdout.close() + + if loader: + if not args.core and not args.save_core: + loader.remove_tmp_file(core_fname) + loader.cleanup() + print 'Done!' + + +def main(): + parser = argparse.ArgumentParser(description='espcoredump.py v%s - ESP32 Core Dump Utility' % __version__, prog='espcoredump') + + parser.add_argument('--chip', '-c', + help='Target chip type', + choices=['auto', 'esp32'], + default=os.environ.get('ESPTOOL_CHIP', 'auto')) + + parser.add_argument( + '--port', '-p', + help='Serial port device', + default=os.environ.get('ESPTOOL_PORT', esptool.ESPLoader.DEFAULT_PORT)) + + parser.add_argument( + '--baud', '-b', + help='Serial port baud rate used when flashing/reading', + type=int, + default=os.environ.get('ESPTOOL_BAUD', esptool.ESPLoader.ESP_ROM_BAUD)) + + subparsers = parser.add_subparsers( + dest='operation', + help='Run coredumper {command} -h for additional help') + + parser_debug_coredump = subparsers.add_parser( + 'dbg_corefile', + help='Starts GDB debugging session with specified corefile') + parser_debug_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') + parser_debug_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) + parser_debug_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), raw (raw) or base64-encoded (b64) binary', type=str, default='elf') + parser_debug_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000) + parser_debug_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. Ignored with "-c"', type=str) + parser_debug_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str) + + parser_info_coredump = subparsers.add_parser( + 'info_corefile', + help='Print core dump info from file') + parser_info_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') + parser_info_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) + parser_info_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), raw (raw) or base64-encoded (b64) binary', type=str, default='elf') + parser_info_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000) + parser_info_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. Does not work with "-c"', type=str) + parser_info_coredump.add_argument('--print-mem', '-m', help='Print memory dump', action='store_true') + parser_info_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str) + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation + + args = parser.parse_args() + + print 'espcoredump.py v%s' % __version__ + + operation_func = globals()[args.operation] + operation_func(args) + + +if __name__ == '__main__': + try: + main() + except ESPCoreDumpError as e: + print '\nA fatal error occurred: %s' % e + sys.exit(2) diff --git a/components/espcoredump/test/component.mk b/components/espcoredump/test/component.mk new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/espcoredump/test/test_core_dump.c b/components/espcoredump/test/test_core_dump.c new file mode 100644 index 0000000000..41137f9302 --- /dev/null +++ b/components/espcoredump/test/test_core_dump.c @@ -0,0 +1,105 @@ +/* Application For Core Dumps Generation + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" + + +// task crash indicators +#define TCI_NULL_PTR 0x1 +#define TCI_UNALIGN_PTR 0x2 +#define TCI_FAIL_ASSERT 0x4 + +volatile unsigned long crash_flags = TCI_UNALIGN_PTR; + +void bad_ptr_func() +{ + unsigned long *ptr = (unsigned long *)0; + volatile int cnt; + int i = 0; + + for (i = 0; i < 1000; i++) { + cnt++; + } + + if(crash_flags & TCI_NULL_PTR) { + printf("Write to bad address 0x%lx.\n", (unsigned long)ptr); + *ptr = 0xDEADBEEF; + } +} + +void bad_ptr_task(void *pvParameter) +{ + printf("Task 'bad_ptr_task' start.\n"); + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Task 'bad_ptr_task' run.\n"); + bad_ptr_func(); + } + fflush(stdout); +} + +void recur_func() +{ + static int rec_cnt; + unsigned short *ptr = (unsigned short *)0x5; + volatile int cnt; + int i = 0; + + if (rec_cnt++ > 2) { + return; + } + for (i = 0; i < 4; i++) { + cnt++; + if(i == 2) { + recur_func(); + break; + } + } + + if(crash_flags & TCI_UNALIGN_PTR) { + printf("Write to unaligned address 0x%lx.\n", (unsigned long)ptr); + *ptr = 0xDEAD; + } +} + +void unaligned_ptr_task(void *pvParameter) +{ + printf("Task 'unaligned_ptr_task' start.\n"); + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Task 'unaligned_ptr_task' run.\n"); + recur_func(); + } + fflush(stdout); +} + +void failed_assert_task(void *pvParameter) +{ + printf("Task 'failed_assert_task' start.\n"); + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Task 'failed_assert_task' run.\n"); + if(crash_flags & TCI_FAIL_ASSERT) { + printf("Assert.\n"); + assert(0); + } + } + fflush(stdout); +} + +void app_main() +{ + nvs_flash_init(); + xTaskCreate(&bad_ptr_task, "bad_ptr_task", 2048, NULL, 5, NULL); + xTaskCreatePinnedToCore(&unaligned_ptr_task, "unaligned_ptr_task", 2048, NULL, 7, NULL, 1); + xTaskCreatePinnedToCore(&failed_assert_task, "failed_assert_task", 2048, NULL, 10, NULL, 0); +} diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index 8bab51225e..edff88c55d 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -121,4 +121,101 @@ config ESPTOOLPY_FLASHSIZE default "8MB" if ESPTOOLPY_FLASHSIZE_8MB default "16MB" if ESPTOOLPY_FLASHSIZE_16MB +config ESPTOOLPY_FLASHSIZE_DETECT + bool "Detect flash size when flashing bootloader" + default y + help + If this option is set, 'make flash' targets will automatically detect + the flash size and update the bootloader image when flashing. + +choice ESPTOOLPY_BEFORE + prompt "Before flashing" + default ESPTOOLPY_BEFORE_RESET + help + Configure whether esptool.py should reset the ESP32 before flashing. + + Automatic resetting depends on the RTS & DTR signals being + wired from the serial port to the ESP32. Most USB development + boards do this internally. + + The "Reset with ESP32R0 Windows workaround" option works + around an automatic reset bug in hardware, when using Windows + with some development boards. This fix only works if you're + using a silicon revision 0 ESP32. + +config ESPTOOLPY_BEFORE_RESET + bool "Reset to bootloader" +config ESPTOOLPY_BEFORE_NORESET + bool "No reset" +config ESPTOOLPY_BEFORE_ESP32R0 + bool "Reset with ESP32R0 Windows workaround" +endchoice + +config ESPTOOLPY_BEFORE + string + default "default_reset" if ESPTOOLPY_BEFORE_RESET + default "no_reset" if ESPTOOLPY_BEFORE_NORESET + default "esp32r0" if ESPTOOLPY_BEFORE_ESP32R0 + +choice ESPTOOLPY_AFTER + prompt "After flashing" + default ESPTOOLPY_AFTER_RESET + help + Configure whether esptool.py should reset the ESP32 after flashing. + + Automatic resetting depends on the RTS & DTR signals being + wired from the serial port to the ESP32. Most USB development + boards do this internally. + +config ESPTOOLPY_AFTER_RESET + bool "Reset after flashing" +config ESPTOOLPY_AFTER_NORESET + bool "Stay in bootloader" +endchoice + +config ESPTOOLPY_AFTER + string + default "hard_reset" if ESPTOOLPY_AFTER_RESET + default "no_reset" if ESPTOOLPY_AFTER_NORESET + +choice MONITOR_BAUD + prompt "'make monitor' baud rate" + default MONITOR_BAUD_115200B + help + Baud rate to use when running 'make monitor' to view serial output + from a running chip. + + Can override by setting the MONITORBAUD environment variable. + +config MONITOR_BAUD_9600B + bool "9600 bps" +config MONITOR_BAUD_57600B + bool "57600 bps" +config MONITOR_BAUD_115200B + bool "115200 bps" +config MONITOR_BAUD_230400B + bool "230400 bps" +config MONITOR_BAUD_921600B + bool "921600 bps" +config MONITOR_BAUD_2MB + bool "2 Mbps" +config MONITOR_BAUD_OTHER + bool "Custom baud rate" + +endchoice + +config MONITOR_BAUD_OTHER_VAL + int "Custom baud rate value" if MONITOR_BAUD_OTHER + default 115200 + +config MONITOR_BAUD + int + default 9600 if MONITOR_BAUD_9600B + default 57600 if MONITOR_BAUD_57600B + default 115200 if MONITOR_BAUD_115200B + default 230400 if MONITOR_BAUD_230400B + default 921600 if MONITOR_BAUD_921600B + default 2000000 if MONITOR_BAUD_2MB + default MONITOR_BAUD_OTHER_VAL if MONITOR_BAUD_OTHER + endmenu diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index b5992a4d96..e249d14432 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -13,7 +13,7 @@ PYTHON ?= $(call dequote,$(CONFIG_PYTHON)) # ESPTOOLPY_SRC := $(COMPONENT_PATH)/esptool/esptool.py ESPTOOLPY := $(PYTHON) $(ESPTOOLPY_SRC) --chip esp32 -ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) +ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) --before $(CONFIG_ESPTOOLPY_BEFORE) --after $(CONFIG_ESPTOOLPY_AFTER) # Supporting esptool command line tools ESPEFUSEPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espefuse.py @@ -21,10 +21,15 @@ ESPSECUREPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espsecure.py export ESPSECUREPY # is used in bootloader_support component ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) +ifdef CONFIG_ESPTOOLPY_FLASHSIZE_DETECT +ESPTOOL_WRITE_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size detect +else +ESPTOOL_WRITE_FLASH_OPTIONS := $(ESPTOOL_FLASH_OPTIONS) +endif ESPTOOL_ELF2IMAGE_OPTIONS := -ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) +ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_WRITE_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) @@ -62,4 +67,12 @@ erase_flash: @echo "Erasing entire flash..." $(ESPTOOLPY_SERIAL) erase_flash +MONITORBAUD ?= $(CONFIG_MONITOR_BAUD) + +# note: if you want to run miniterm from command line, can simply run +# miniterm.py on the console. The '$(PYTHON) -m serial.tools.miniterm' +# is to allow for the $(PYTHON) variable overriding the python path. +monitor: $(call prereq_if_explicit,%flash) + $(PYTHON) -m serial.tools.miniterm --rts 0 --dtr 0 --raw $(ESPPORT) $(MONITORBAUD) + .PHONY: erase_flash diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index adc914b91a..fe69994270 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit adc914b91ac6d2cfd1ace56307b4374eb9439e14 +Subproject commit fe69994270e2a450aad3e94a409b58460b1a214f diff --git a/components/ethernet/Kconfig b/components/ethernet/Kconfig index 9eb897cbba..0ea94eaafd 100644 --- a/components/ethernet/Kconfig +++ b/components/ethernet/Kconfig @@ -1,8 +1,8 @@ menuconfig ETHERNET - bool "Enable Ethernet" + bool "Ethernet" default n help - Enable this option to enable ethernet driver and show the menu with ethernet features. + Select this option to enable ethernet driver and show the submenu with ethernet features. config DMA_RX_BUF_NUM int "DMA Rx Buf Num" diff --git a/components/ethernet/emac_common.h b/components/ethernet/emac_common.h index 774c287ad5..957337d2a7 100644 --- a/components/ethernet/emac_common.h +++ b/components/ethernet/emac_common.h @@ -72,6 +72,9 @@ struct emac_config_data { eth_phy_check_init_func emac_phy_check_init; eth_phy_get_speed_mode_func emac_phy_get_speed_mode; eth_phy_get_duplex_mode_func emac_phy_get_duplex_mode; + bool emac_flow_ctrl_enable; + bool emac_flow_ctrl_partner_support; + eth_phy_get_partner_pause_enable_func emac_phy_get_partner_pause_enable; }; enum emac_post_type { @@ -109,6 +112,13 @@ struct emac_close_cmd { #define DMA_RX_BUF_SIZE 1600 #define DMA_TX_BUF_SIZE 1600 +//rest buf num +#define FLOW_CONTROL_HIGH_WATERMARK 3 +//used buf num +#define FLOW_CONTROL_LOW_WATERMARK 6 + +#define PHY_LINK_CHECK_NUM 5 + #define EMAC_CMD_OK 0 #define EMAC_CMD_FAIL -1 diff --git a/components/ethernet/emac_dev.c b/components/ethernet/emac_dev.c index 244da08432..ba61bb750b 100644 --- a/components/ethernet/emac_dev.c +++ b/components/ethernet/emac_dev.c @@ -34,6 +34,24 @@ static const char *TAG = "emac"; +void emac_enable_flowctrl(void) +{ + REG_SET_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_TRANSMIT_FLOW_CONTROL_ENABLE); + REG_SET_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_RECEIVE_FLOW_CONTROL_ENABLE); + REG_CLR_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_DISABLE_ZERO_QUANTA_PAUSE); + REG_SET_FIELD(EMAC_GMACFLOWCONTROL_REG, EMAC_PAUSE_TIME, 0x1648); + REG_SET_FIELD(EMAC_GMACFLOWCONTROL_REG, EMAC_PAUSE_LOW_THRESHOLD, 0x1); +} + +void emac_disable_flowctrl(void) +{ + REG_CLR_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_TRANSMIT_FLOW_CONTROL_ENABLE); + REG_CLR_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_RECEIVE_FLOW_CONTROL_ENABLE); + REG_CLR_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_DISABLE_ZERO_QUANTA_PAUSE); + REG_SET_FIELD(EMAC_GMACFLOWCONTROL_REG, EMAC_PAUSE_TIME, 0); + REG_SET_FIELD(EMAC_GMACFLOWCONTROL_REG, EMAC_PAUSE_LOW_THRESHOLD, 0); +} + void emac_enable_dma_tx(void) { REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_START_STOP_TRANSMISSION_COMMAND); @@ -100,18 +118,21 @@ void emac_dma_init(void) REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_FORWARD_UNDERSIZED_GOOD_FRAMES); REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_OPERATE_SECOND_FRAME); REG_SET_FIELD(EMAC_DMABUSMODE_REG, EMAC_PROG_BURST_LEN, 4); - REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG,EMAC_DMAOPERATION_MODE_REG); + REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_DMAOPERATION_MODE_REG); +} + +void emac_mac_enable_txrx(void) +{ + REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACRX); + REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACTX); } void emac_mac_init(void) { REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX); REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACMIIGMII); - REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); + REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); REG_SET_BIT(EMAC_GMACFRAMEFILTER_REG, EMAC_PROMISCUOUS_MODE); - - REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACRX); - REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACTX); } void emac_set_clk_rmii(void) diff --git a/components/ethernet/emac_dev.h b/components/ethernet/emac_dev.h index bc04d8d9e2..dc1045b92f 100644 --- a/components/ethernet/emac_dev.h +++ b/components/ethernet/emac_dev.h @@ -52,6 +52,9 @@ void emac_enable_dma_tx(void); void emac_enable_dma_rx(void); void emac_disable_dma_tx(void); void emac_disable_dma_rx(void); +void emac_enable_flowctrl(void); +void emac_disable_flowctrl(void); +void emac_mac_enable_txrx(void); uint32_t inline emac_read_tx_cur_reg(void) { @@ -77,22 +80,32 @@ void inline emac_poll_rx_cmd(void) void inline emac_disable_rx_intr(void) { - REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_INTERRUPT_ENABLE); + REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG, EMAC_RECEIVE_INTERRUPT_ENABLE); } void inline emac_enable_rx_intr(void) { - REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_INTERRUPT_ENABLE); + REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG, EMAC_RECEIVE_INTERRUPT_ENABLE); } void inline emac_disable_rx_unavail_intr(void) { - REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); + REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG, EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); } void inline emac_enable_rx_unavail_intr(void) { - REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); + REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG, EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); +} + +void IRAM_ATTR inline emac_send_pause_frame_enable(void) +{ + REG_SET_BIT(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_SBD_FLOWCTRL); +} + +void inline emac_send_pause_zero_frame_enable(void) +{ + REG_CLR_BIT(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_SBD_FLOWCTRL); } #ifdef __cplusplus diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index ab2ca8964c..9446e8ee35 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -68,6 +68,7 @@ static TimerHandle_t emac_timer = NULL; static SemaphoreHandle_t emac_rx_xMutex = NULL; static SemaphoreHandle_t emac_tx_xMutex = NULL; static const char *TAG = "emac"; +static bool pause_send = false; static esp_err_t emac_ioctl(emac_sig_t sig, emac_par_t par); esp_err_t emac_post(emac_sig_t sig, emac_par_t par); @@ -96,9 +97,9 @@ static void emac_clean_tx_desc(struct dma_extended_desc *tx_desc) tx_desc->basic.desc0 = 0; } -static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc ,uint32_t buf_ptr) +static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc , uint32_t buf_ptr) { - if(buf_ptr != 0) { + if (buf_ptr != 0) { rx_desc->basic.desc2 = buf_ptr; } rx_desc->basic.desc1 = EMAC_DESC_RX_SECOND_ADDR_CHAIN | DMA_RX_BUF_SIZE; @@ -215,6 +216,8 @@ static void emac_set_user_config_data(eth_config_t *config ) emac_config.emac_phy_check_init = config->phy_check_init; emac_config.emac_phy_get_speed_mode = config->phy_get_speed_mode; emac_config.emac_phy_get_duplex_mode = config->phy_get_duplex_mode; + emac_config.emac_flow_ctrl_enable = config->flow_ctrl_enable; + emac_config.emac_phy_get_partner_pause_enable = config->phy_get_partner_pause_enable; } static void emac_enable_intr() @@ -276,6 +279,11 @@ static esp_err_t emac_verify_args(void) ret = ESP_FAIL; } + if (emac_config.emac_flow_ctrl_enable == true && emac_config.emac_phy_get_partner_pause_enable == NULL) { + ESP_LOGE(TAG, "phy get partner pause enable func is null"); + ret = ESP_FAIL; + } + return ret; } @@ -293,13 +301,13 @@ static void emac_process_tx(void) { uint32_t cur_tx_desc = emac_read_tx_cur_reg(); - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY ); - while (((uint32_t) &(emac_config.dma_etx[emac_config.dirty_tx].basic.desc0) != cur_tx_desc)) { + while (((uint32_t) & (emac_config.dma_etx[emac_config.dirty_tx].basic.desc0) != cur_tx_desc)) { emac_clean_tx_desc(&(emac_config.dma_etx[emac_config.dirty_tx])); emac_config.dirty_tx = (emac_config.dirty_tx + 1) % DMA_TX_BUF_NUM; emac_config.cnt_tx --; @@ -317,21 +325,43 @@ void esp_eth_free_rx_buf(void *buf) { xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.cur_rx]),(uint32_t) buf); + emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.cur_rx]), (uint32_t) buf); emac_config.cur_rx = (emac_config.cur_rx + 1) % DMA_RX_BUF_NUM; emac_config.cnt_rx--; - if(emac_config.cnt_rx < 0) { - ESP_LOGE(TAG, "emac rx buf err!!\n"); + if (emac_config.cnt_rx < 0) { + ESP_LOGE(TAG, "emac rx buf err!!\n"); } emac_poll_rx_cmd(); xSemaphoreGiveRecursive( emac_rx_xMutex ); + + if (emac_config.emac_flow_ctrl_partner_support == true) { + portENTER_CRITICAL(&g_emac_mux); + if (pause_send == true && emac_config.cnt_rx < FLOW_CONTROL_LOW_WATERMARK) { + emac_send_pause_zero_frame_enable(); + pause_send = false; + } + portEXIT_CRITICAL(&g_emac_mux); + } +} + +static uint32_t IRAM_ATTR emac_get_rxbuf_count_in_intr(void) +{ + uint32_t cnt = 0; + uint32_t cur_rx_desc = emac_read_rx_cur_reg(); + struct dma_extended_desc *cur_desc = (struct dma_extended_desc *)cur_rx_desc; + + while (cur_desc->basic.desc0 == EMAC_DESC_RX_OWN) { + cnt++; + cur_desc = (struct dma_extended_desc *)cur_desc->basic.desc3; + } + return cnt; } #if CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE static void emac_process_rx(void) { - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } uint32_t cur_rx_desc = emac_read_rx_cur_reg(); @@ -341,7 +371,7 @@ static void emac_process_rx(void) emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]),(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); + emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]), (emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; //if open this ,one intr can do many intrs ? @@ -353,14 +383,14 @@ static void emac_process_rx(void) static void emac_process_rx_unavail(void) { - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } uint32_t dirty_cnt = 0; while (dirty_cnt < DMA_RX_BUF_NUM) { - if(emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) { + if (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) { break; } @@ -369,7 +399,7 @@ static void emac_process_rx_unavail(void) emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]),(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); + emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]), (emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; } emac_enable_rx_intr(); @@ -380,7 +410,7 @@ static void emac_process_rx_unavail(void) #else static void emac_process_rx_unavail(void) { - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } @@ -388,11 +418,11 @@ static void emac_process_rx_unavail(void) while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { - //copy data to lwip + //copy data to lwip emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); emac_config.cnt_rx++; - if(emac_config.cnt_rx > DMA_RX_BUF_NUM) { + if (emac_config.cnt_rx > DMA_RX_BUF_NUM) { ESP_LOGE(TAG, "emac rx unavail buf err !!\n"); } emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; @@ -404,7 +434,7 @@ static void emac_process_rx_unavail(void) static void emac_process_rx(void) { - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } @@ -412,16 +442,16 @@ static void emac_process_rx(void) xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); - if(((uint32_t) &(emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) { + if (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) { - while (((uint32_t) &(emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc) && emac_config.cnt_rx < DMA_RX_BUF_NUM ) { + while (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc) && emac_config.cnt_rx < DMA_RX_BUF_NUM ) { //copy data to lwip emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), - (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); emac_config.cnt_rx++; - if(emac_config.cnt_rx > DMA_RX_BUF_NUM ) { + if (emac_config.cnt_rx > DMA_RX_BUF_NUM ) { ESP_LOGE(TAG, "emac rx buf err!!\n"); } emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; @@ -429,16 +459,16 @@ static void emac_process_rx(void) cur_rx_desc = emac_read_rx_cur_reg(); } } else { - if(emac_config.cnt_rx < DMA_RX_BUF_NUM) { - if((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 & EMAC_DESC_RX_OWN) == 0) { + if (emac_config.cnt_rx < DMA_RX_BUF_NUM) { + if ((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 & EMAC_DESC_RX_OWN) == 0) { while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { //copy data to lwip emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), - (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); emac_config.cnt_rx++; - if(emac_config.cnt_rx > DMA_RX_BUF_NUM) { - ESP_LOGE(TAG,"emac rx buf err!!!\n"); + if (emac_config.cnt_rx > DMA_RX_BUF_NUM) { + ESP_LOGE(TAG, "emac rx buf err!!!\n"); } emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; @@ -462,12 +492,18 @@ static void IRAM_ATTR emac_process_intr(void *arg) if (event & EMAC_RECV_INT) { emac_disable_rx_intr(); + if (emac_config.emac_flow_ctrl_partner_support == true) { + if (emac_get_rxbuf_count_in_intr() < FLOW_CONTROL_HIGH_WATERMARK && pause_send == false ) { + pause_send = true; + emac_send_pause_frame_enable(); + } + } emac_post(SIG_EMAC_RX_DONE, 0); } if (event & EMAC_RECV_BUF_UNAVAIL) { emac_disable_rx_unavail_intr(); - emac_post(SIG_EMAC_RX_UNAVAIL,0); + emac_post(SIG_EMAC_RX_UNAVAIL, 0); } if (event & EMAC_TRANS_INT) { @@ -475,25 +511,43 @@ static void IRAM_ATTR emac_process_intr(void *arg) } } +static void emac_set_macaddr_reg(void) +{ + REG_SET_FIELD(EMAC_GMACADDR0HIGH_REG, EMAC_MAC_ADDRESS0_HI, (emac_config.macaddr[0] << 8) | (emac_config.macaddr[1])); + REG_WRITE(EMAC_GMACADDR0LOW_REG, (emac_config.macaddr[2] << 24) | (emac_config.macaddr[3] << 16) | (emac_config.macaddr[4] << 8) | (emac_config.macaddr[5])); +} + static void emac_check_phy_init(void) { emac_config.emac_phy_check_init(); - if(emac_config.emac_phy_get_duplex_mode() == ETH_MDOE_FULLDUPLEX) { + if (emac_config.emac_phy_get_duplex_mode() == ETH_MDOE_FULLDUPLEX) { REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX); } else { REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX); } - if(emac_config.emac_phy_get_speed_mode() == ETH_SPEED_MODE_100M) { + if (emac_config.emac_phy_get_speed_mode() == ETH_SPEED_MODE_100M) { REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); } else { REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); } - - emac_mac_init(); + if (emac_config.emac_flow_ctrl_enable == true) { + if (emac_config.emac_phy_get_partner_pause_enable() == true && emac_config.emac_phy_get_duplex_mode() == ETH_MDOE_FULLDUPLEX) { + emac_enable_flowctrl(); + emac_config.emac_flow_ctrl_partner_support = true; + } else { + emac_disable_flowctrl(); + emac_config.emac_flow_ctrl_partner_support = false; + } + } else { + emac_disable_flowctrl(); + emac_config.emac_flow_ctrl_partner_support = false; + } + emac_mac_enable_txrx(); } static void emac_process_link_updown(bool link_status) { system_event_t evt; + uint8_t i = 0; emac_config.phy_link_up = link_status; @@ -502,6 +556,10 @@ static void emac_process_link_updown(bool link_status) ESP_LOGI(TAG, "eth link_up!!!"); emac_enable_dma_tx(); emac_enable_dma_rx(); + for (i = 0; i < PHY_LINK_CHECK_NUM; i++) { + emac_check_phy_init(); + } + evt.event_id = SYSTEM_EVENT_ETH_CONNECTED; } else { ESP_LOGI(TAG, "eth link_down!!!"); @@ -534,7 +592,7 @@ esp_err_t esp_eth_tx(uint8_t *buf, uint16_t size) } xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY ); - if (emac_config.cnt_tx == DMA_TX_BUF_NUM -1) { + if (emac_config.cnt_tx == DMA_TX_BUF_NUM - 1) { ESP_LOGD(TAG, "tx buf full"); ret = ERR_MEM; goto _exit; @@ -557,13 +615,13 @@ _exit: static void emac_init_default_data(void) { - memset((uint8_t *)&emac_config, 0,sizeof(struct emac_config_data)); + memset((uint8_t *)&emac_config, 0, sizeof(struct emac_config_data)); } -void emac_process_link_check(void) +void emac_process_link_check(void) { if (emac_config.emac_status != EMAC_RUNTIME_START || - emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) { + emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) { return; } @@ -580,12 +638,12 @@ void emac_process_link_check(void) void emac_link_check_func(void *pv_parameters) { - emac_post(SIG_EMAC_CHECK_LINK,0); + emac_post(SIG_EMAC_CHECK_LINK, 0); } static bool emac_link_check_timer_init(void) { - emac_timer = xTimerCreate("emac_timer", (2000 / portTICK_RATE_MS), + emac_timer = xTimerCreate("emac_timer", (2000 / portTICK_PERIOD_MS), pdTRUE, (void *)rand(), emac_link_check_func); if (emac_timer == NULL) { return false; @@ -628,15 +686,19 @@ static void emac_start(void *param) cmd->err = EMAC_CMD_OK; emac_enable_clk(true); + emac_reset(); emac_macaddr_init(); emac_check_mac_addr(); emac_set_mac_addr(); + emac_set_macaddr_reg(); emac_set_tx_base_reg(); emac_set_rx_base_reg(); + emac_mac_init(); + emac_config.phy_init(); //ptp TODO @@ -817,7 +879,7 @@ void emac_task(void *pv) esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par) { - if(sig <= SIG_EMAC_RX_DONE) { + if (sig <= SIG_EMAC_RX_DONE) { if (emac_sig_cnt[sig]) { return ESP_OK; } else { @@ -830,21 +892,23 @@ esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par) ret = xQueueSendFromISR(emac_xqueue, &evt, &tmp); - if(tmp != pdFALSE) { + if (tmp != pdFALSE) { portYIELD_FROM_ISR(); } - if(ret != pdPASS) { + if (ret != pdPASS) { return ESP_FAIL; } } } else { + portENTER_CRITICAL(&g_emac_mux); emac_sig_cnt[sig]++; + portEXIT_CRITICAL(&g_emac_mux); emac_event_t evt; evt.sig = sig; evt.par = par; - if (xQueueSend(emac_xqueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(emac_xqueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { return ESP_FAIL; } } @@ -898,10 +962,8 @@ esp_err_t esp_eth_init(eth_config_t *config) emac_xqueue = xQueueCreate(EMAC_EVT_QNUM, sizeof(emac_event_t)); xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY, &emac_task_hdl); - esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL); - - emac_reset(); emac_enable_clk(false); + esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL); emac_config.emac_status = EMAC_RUNTIME_INIT; diff --git a/components/ethernet/include/esp_eth.h b/components/ethernet/include/esp_eth.h index 2aae9d2024..e36c3ebfe6 100644 --- a/components/ethernet/include/esp_eth.h +++ b/components/ethernet/include/esp_eth.h @@ -79,6 +79,7 @@ typedef eth_duplex_mode_t (*eth_phy_get_duplex_mode_func)(void); typedef void (*eth_phy_func)(void); typedef esp_err_t (*eth_tcpip_input_func)(void *buffer, uint16_t len, void *eb); typedef void (*eth_gpio_config_func)(void); +typedef bool (*eth_phy_get_partner_pause_enable_func)(void); /** @@ -95,6 +96,9 @@ typedef struct { eth_phy_get_speed_mode_func phy_get_speed_mode; /*!< phy check init func */ eth_phy_get_duplex_mode_func phy_get_duplex_mode; /*!< phy check init func */ eth_gpio_config_func gpio_config; /*!< gpio config func */ + bool flow_ctrl_enable; /*!< flag of flow ctrl enable */ + eth_phy_get_partner_pause_enable_func phy_get_partner_pause_enable; /*!< get partner pause enable */ + } eth_config_t; /** diff --git a/components/fatfs/component.mk b/components/fatfs/component.mk new file mode 100644 index 0000000000..591e080c62 --- /dev/null +++ b/components/fatfs/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_SRCDIRS := src/option src diff --git a/components/fatfs/src/00history.txt b/components/fatfs/src/00history.txt new file mode 100644 index 0000000000..49aac282b2 --- /dev/null +++ b/components/fatfs/src/00history.txt @@ -0,0 +1,279 @@ +---------------------------------------------------------------------------- + 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 on 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) + + Improved f_rename() to 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) + diff --git a/components/fatfs/src/00readme.txt b/components/fatfs/src/00readme.txt new file mode 100644 index 0000000000..42426a4011 --- /dev/null +++ b/components/fatfs/src/00readme.txt @@ -0,0 +1,21 @@ +FatFs Module Source Files R0.12a + + +FILES + + 00readme.txt This file. + history.txt Revision history. + ffconf.h Configuration file for FatFs module. + ff.h Common include file for FatFs and application module. + ff.c FatFs 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. + integer.h Integer type definitions for FatFs. + option Optional external 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 not depend on any specific + storage device. You have to provide a low level disk I/O module that written + to control the target storage device. + diff --git a/components/fatfs/src/diskio.c b/components/fatfs/src/diskio.c new file mode 100644 index 0000000000..004cfc5bea --- /dev/null +++ b/components/fatfs/src/diskio.c @@ -0,0 +1,134 @@ +/*-----------------------------------------------------------------------*/ +/* 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 +#include "diskio.h" /* FatFs lower layer API */ +#include "ffconf.h" +#include "ff.h" +#include "sdmmc_cmd.h" +#include "esp_log.h" +#include +#include + +static const char* TAG = "ff_diskio"; +static ff_diskio_impl_t s_impls[_VOLUMES] = { { 0 } }; +static sdmmc_card_t* s_cards[_VOLUMES] = { NULL }; + +PARTITION VolToPart[] = { + {0, 1}, /* Logical drive 0 ==> Physical drive 0, 1st partition */ + {1, 0} /* Logical drive 1 ==> Physical drive 1, auto detection */ +}; + +void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl) +{ + assert(pdrv < _VOLUMES); + memcpy(&s_impls[pdrv], discio_impl, sizeof(ff_diskio_impl_t)); +} + +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, DWORD sector, UINT count) +{ + return s_impls[pdrv].read(pdrv, buff, sector, count); +} +DRESULT ff_disk_write (BYTE pdrv, const BYTE* buff, DWORD 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 = gmtime(&t); + 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); +} + +DSTATUS ff_sdmmc_initialize (BYTE pdrv) +{ + return 0; +} + +DSTATUS ff_sdmmc_status (BYTE 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 (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 (err != ESP_OK) { + ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err); + return RES_ERROR; + } + return RES_OK; +} + +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: + *((uint32_t*) buff) = card->csd.capacity; + return RES_OK; + case GET_SECTOR_SIZE: + *((uint32_t*) buff) = card->csd.sector_size; + return RES_OK; + case GET_BLOCK_SIZE: + return RES_ERROR; + } + return RES_ERROR; +} + +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; + ff_diskio_register(pdrv, &sdmmc_impl); +} + diff --git a/components/fatfs/src/diskio.h b/components/fatfs/src/diskio.h new file mode 100644 index 0000000000..7c224809f9 --- /dev/null +++ b/components/fatfs/src/diskio.h @@ -0,0 +1,120 @@ +/*-----------------------------------------------------------------------/ +/ Low level disk interface modlue include file (C)ChaN, 2014 / +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO_DEFINED +#define _DISKIO_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" +#include "sdmmc_cmd.h" +#include "driver/sdmmc_host.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 */ + + +/* 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 + + +DSTATUS disk_initialize (BYTE pdrv); +DSTATUS disk_status (BYTE pdrv); +DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); +DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); + +/** + * Structure of pointers to disk IO driver functions. + * + * See FatFs documentation for details about these functions + */ +typedef struct { + DSTATUS (*init) (BYTE pdrv); /*!< disk initialization function */ + DSTATUS (*status) (BYTE pdrv); /*!< disk status check function */ + DRESULT (*read) (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); /*!< sector read function */ + DRESULT (*write) (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); /*!< sector write function */ + DRESULT (*ioctl) (BYTE pdrv, BYTE cmd, void* buff); /*!< function to get info about disk and do some misc operations */ +} ff_diskio_impl_t; + +/** + * Register 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 + */ +void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl); + +/** + * 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(BYTE pdrv, sdmmc_card_t* card); + +/* 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 _FS_READONLY == 0) */ +#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ +#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _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 diff --git a/components/fatfs/src/esp_vfs_fat.h b/components/fatfs/src/esp_vfs_fat.h new file mode 100644 index 0000000000..087d6cf77e --- /dev/null +++ b/components/fatfs/src/esp_vfs_fat.h @@ -0,0 +1,107 @@ +// Copyright 2015-2016 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 +#include "esp_err.h" +#include "driver/gpio.h" +#include "driver/sdmmc_types.h" +#include "driver/sdmmc_host.h" +#include "ff.h" + +/** + * @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. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS + */ +esp_err_t esp_vfs_fat_unregister(); + +/** + * @brief Configuration arguments for esp_vfs_fat_sdmmc_mount function + */ +typedef struct { + bool format_if_mount_failed; ///< If FAT partition can not be mounted, and this parameter is true, create partition table and format the filesystem + int max_files; ///< Max number of open files +} 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 SD/MMC peripheral with configuration in host_config + * - initializes SD/MMC card with configuration in slot_config + * - mounts FAT partition on SD/MMC 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. + * + * @param base_path path where partition should be registered (e.g. "/sdcard") + * @param host_config pointer to structure describing SDMMC host + * @param slot_config pointer to structure with extra SDMMC slot configuration + * @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 host, SDMMC protocol, or FATFS drivers + */ +esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, + const sdmmc_host_t* host_config, + const sdmmc_slot_config_t* slot_config, + const esp_vfs_fat_sdmmc_mount_config_t* mount_config, + sdmmc_card_t** out_card); + +/** + * @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_sdmmc_mount + * + * @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(); diff --git a/components/fatfs/src/ff.c b/components/fatfs/src/ff.c new file mode 100644 index 0000000000..0d7d7366bf --- /dev/null +++ b/components/fatfs/src/ff.c @@ -0,0 +1,6042 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT file system module R0.12b / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2016, 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. +/----------------------------------------------------------------------------*/ + + +#define FF_DEFINE_DIR +#include "ff.h" /* Declarations of FatFs API */ +#include "diskio.h" /* Declarations of device I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 68020 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* Reentrancy related */ +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area cannot be used at thread-safe configuration +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res +#endif + + + +/* Definitions of sector size */ +#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096) +#error Wrong sector size configuration +#endif +#if _MAX_SS == _MIN_SS +#define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + + +/* Timestamp */ +#if _FS_NORTC == 1 +#if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31 +#error Invalid _FS_NORTC settings +#endif +#define GET_FATTIME() ((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16) +#else +#define GET_FATTIME() get_fattime() +#endif + + +/* File lock controls */ +#if _FS_LOCK != 0 +#if _FS_READONLY +#error _FS_LOCK must be 0 at read-only configuration +#endif +typedef struct { + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ + DWORD clu; /* Object ID 2, directory (0:root) */ + DWORD ofs; /* Object ID 3, directory offset */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + + +/* DBCS code ranges and SBCS upper conversion tables */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 771 /* KBL */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Latin 1 */ +#define _DF1S 0 +#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \ + 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \ + 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \ + 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \ + 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 860 /* Portuguese */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \ + 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 861 /* Icelandic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 863 /* Canadian-French */ +#define _DF1S 0 +#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \ + 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \ + 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 864 /* Arabic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 865 /* Nordic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 869 /* Greek 2 */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \ + 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \ + 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \ + 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#if _USE_LFN != 0 +#error Cannot enable LFN without valid code page. +#endif +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + +/* Character code support macros */ +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) +#define IsDigit(c) (((c)>='0')&&((c)<='9')) + +#if _DF1S != 0 /* Code page is DBCS */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* Code page is SBCS */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + +/* File attribute bits (internal use) */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* File access control and file status flags (internal use) */ +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ +#define FA_MODIFIED 0x40 /* File has been modified */ +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ + + +/* Name status flags */ +#define NSFLAG 11 /* Index of name status byte in fn[] */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ +#define NS_NOLFN 0x40 /* Do not find LFN */ +#define NS_NONAME 0x80 /* Not followed */ + + +/* Limits and boundaries (differ from specs but correct for real DOS/Windows) */ +#define MAX_FAT12 0xFF5 /* Maximum number of FAT12 clusters */ +#define MAX_FAT16 0xFFF5 /* Maximum number of FAT16 clusters */ +#define MAX_FAT32 0xFFFFFF5 /* Maximum number of FAT32 clusters */ +#define MAX_EXFAT 0x7FFFFFFD /* Maximum number of exFAT clusters (limited by implementation) */ +#define MAX_DIR 0x200000 /* Maximum size of FAT directory */ +#define MAX_DIR_EX 0x10000000 /* Maximum size of exFAT directory */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure members because the structure is not binary compatible between +/ different platforms */ + +#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ +#define BS_OEMName 3 /* OEM name (8-byte) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ +#define BPB_RootEntCnt 17 /* Size of root directory area for FAT12/16 [entry] (WORD) */ +#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ +#define BPB_Media 21 /* Media descriptor byte (BYTE) */ +#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ +#define BPB_SecPerTrk 24 /* Track size for int13h [sector] (WORD) */ +#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ +#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ +#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ +#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ +#define BS_NTres 37 /* Error flag (BYTE) */ +#define BS_BootSig 38 /* Extended boot signature (BYTE) */ +#define BS_VolID 39 /* Volume serial number (DWORD) */ +#define BS_VolLab 43 /* Volume label string (8-byte) */ +#define BS_FilSysType 54 /* File system type string (8-byte) */ +#define BS_BootCode 62 /* Boot code (448-byte) */ +#define BS_55AA 510 /* Signature word (WORD) */ + +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ +#define BPB_FSVer32 42 /* FAT32: File system version (WORD) */ +#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ +#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ +#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ +#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ +#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ +#define BS_FilSysType32 82 /* FAT32: File system type string (8-byte) */ +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ + +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */ +#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */ +#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ +#define BPB_RootClusEx 96 /* exFAT: Root directory cluster (DWORD) */ +#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ +#define BPB_FSVerEx 104 /* exFAT: File system version (WORD) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (BYTE) */ +#define BPB_ActFatEx 107 /* exFAT: Active FAT flags (BYTE) */ +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in byte (BYTE) */ +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in sector (BYTE) */ +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ +#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ +#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ +#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ + +#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ +#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ +#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ + +#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ +#define PTE_StHead 1 /* MBR PTE: Start head */ +#define PTE_StSec 2 /* MBR PTE: Start sector */ +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ +#define PTE_System 4 /* MBR PTE: System ID */ +#define PTE_EdHead 5 /* MBR PTE: End head */ +#define PTE_EdSec 6 /* MBR PTE: End sector */ +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ + +#define DIR_Name 0 /* Short file name (11-byte) */ +#define DIR_Attr 11 /* Attribute (BYTE) */ +#define DIR_NTres 12 /* Lower case flag (BYTE) */ +#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ +#define DIR_CrtTime 14 /* Created time (DWORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ +#define DIR_ModTime 22 /* Modified time (DWORD) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ +#define DIR_FileSize 28 /* File size (DWORD) */ +#define LDIR_Ord 0 /* LFN entry order and LLE flag (BYTE) */ +#define LDIR_Attr 11 /* LFN attribute (BYTE) */ +#define LDIR_Type 12 /* LFN type (BYTE) */ +#define LDIR_Chksum 13 /* Checksum of the SFN entry (BYTE) */ +#define LDIR_FstClusLO 26 /* Must be zero (WORD) */ +#define XDIR_Type 0 /* Type of exFAT directory entry (BYTE) */ +#define XDIR_NumLabel 1 /* Number of volume label characters (BYTE) */ +#define XDIR_Label 2 /* Volume label (11-WORD) */ +#define XDIR_CaseSum 4 /* Sum of case conversion table (DWORD) */ +#define XDIR_NumSec 1 /* Number of secondary entries (BYTE) */ +#define XDIR_SetSum 2 /* Sum of the set of directory entries (WORD) */ +#define XDIR_Attr 4 /* File attribute (WORD) */ +#define XDIR_CrtTime 8 /* Created time (DWORD) */ +#define XDIR_ModTime 12 /* Modified time (DWORD) */ +#define XDIR_AccTime 16 /* Last accessed time (DWORD) */ +#define XDIR_CrtTime10 20 /* Created time subsecond (BYTE) */ +#define XDIR_ModTime10 21 /* Modified time subsecond (BYTE) */ +#define XDIR_CrtTZ 22 /* Created timezone (BYTE) */ +#define XDIR_ModTZ 23 /* Modified timezone (BYTE) */ +#define XDIR_AccTZ 24 /* Last accessed timezone (BYTE) */ +#define XDIR_GenFlags 33 /* Gneral secondary flags (WORD) */ +#define XDIR_NumName 35 /* Number of file name characters (BYTE) */ +#define XDIR_NameHash 36 /* Hash of file name (WORD) */ +#define XDIR_ValidFileSize 40 /* Valid file size (QWORD) */ +#define XDIR_FstClus 52 /* First cluster of the file data (DWORD) */ +#define XDIR_FileSize 56 /* File/Directory size (QWORD) */ + +#define SZDIRE 32 /* Size of a directory entry */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ +#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ + + + + + +/*-------------------------------------------------------------------------- + + Module Private Work Area + +---------------------------------------------------------------------------*/ + +/* Remark: Variables here without initial value shall be guaranteed zero/null +/ at start-up. If not, either the linker or start-up routine being used is +/ not compliance with C standard. */ + +#if _VOLUMES < 1 || _VOLUMES > 9 +#error Wrong _VOLUMES setting +#endif +static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +static WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH != 0 && _VOLUMES >= 2 +static BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_LOCK != 0 +static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ +#endif + +#if _USE_LFN == 0 /* Non-LFN configuration */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() +#else +#if _MAX_LFN < 12 || _MAX_LFN > 255 +#error Wrong _MAX_LFN setting +#endif + +#if _USE_LFN == 1 /* LFN enabled with static working buffer */ +#if _FS_EXFAT +static BYTE DirBuf[SZDIRE*19]; /* Directory entry block scratchpad buffer (19 entries in size) */ +#endif +static WCHAR LfnBuf[_MAX_LFN+1]; /* LFN enabled with static working buffer */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() + +#elif _USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[SZDIRE*19]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } +#define FREE_NAMBUF() +#else +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } +#define FREE_NAMBUF() +#endif + +#elif _USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2 + SZDIRE*19); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); } +#define FREE_NAMBUF() ff_memfree(lfn) +#else +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } +#define FREE_NAMBUF() ff_memfree(lfn) +#endif + +#else +#error Wrong _USE_LFN setting +#endif +#endif + +#ifdef _EXCVT +static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for SBCS extended characters */ +#endif + + + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Load/Store multi-byte word in the FAT structure */ +/*-----------------------------------------------------------------------*/ + +static +WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ +{ + WORD rv; + + rv = ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +static +DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ +{ + DWORD rv; + + rv = ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +#if _FS_EXFAT +static +QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ +{ + QWORD rv; + + rv = ptr[7]; + rv = rv << 8 | ptr[6]; + rv = rv << 8 | ptr[5]; + rv = rv << 8 | ptr[4]; + rv = rv << 8 | ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} +#endif + +#if !_FS_READONLY +static +void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +static +void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +#if _FS_EXFAT +static +void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} +#endif +#endif /* !_FS_READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) { + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + + if (cnt) { + do *d++ = *s++; while (--cnt); + } +} + +/* Fill memory block */ +static +void mem_set (void* dst, int val, UINT cnt) { + BYTE *d = (BYTE*)dst; + + do *d++ = (BYTE)val; while (--cnt); +} + +/* Compare memory block */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) { /* ZR:same, NZ:different */ + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + do { + r = *d++ - *s++; + } while (--cnt && r == 0); + + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { /* NZ:contained, ZR:not contained */ + while (*str && *str != chr) str++; + return *str; +} + + + + +#if _FS_REENTRANT +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +static +int lock_fs ( + FATFS* fs /* File system object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS* fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} + +#endif + + + +#if _FS_LOCK != 0 +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dp, /* Directory object pointing the file to be checked */ + int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dp->obj.fs && /* Check if the object matched with an open object */ + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } else { /* Blank entry */ + be = 1; + } + } + if (i == _FS_LOCK) { /* The object is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */ + } + + /* The object has been opened. Reject any open against writing file and all write mode open */ + return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock (void) /* Check if an entry is available for a new object */ +{ + UINT i; + + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + return (i == _FS_LOCK) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ + DIR* dp, /* Directory object pointing the file to register or increment */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_LOCK; i++) { /* Find the object */ + if (Files[i].fs == dp->obj.fs && + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } + + if (i == _FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */ + Files[i].fs = dp->obj.fs; + Files[i].clu = dp->obj.sclust; + Files[i].ofs = dp->dptr; + Files[i].ctr = 0; + } + + if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; +} + + +static +FRESULT dec_lock ( /* Decrement object open counter */ + UINT i /* Semaphore index (1..) */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_LOCK) { /* Shift index number origin from 0 */ + n = Files[i].ctr; + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n > 0) n--; /* Decrement read mode open count */ + Files[i].ctr = n; + if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + res = FR_OK; + } else { + res = FR_INT_ERR; /* Invalid index nunber */ + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} + +#endif /* _FS_LOCK != 0 */ + + + +/*-----------------------------------------------------------------------*/ +/* Move/Flush disk access window in the file system object */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs /* File system object */ +) +{ + DWORD wsect; + UINT nf; + FRESULT res = FR_OK; + + + if (fs->wflag) { /* Write back the sector if it is dirty */ + wsect = fs->winsect; /* Current sector number */ + if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fs->wflag = 0; + if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */ + for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } + } + return res; +} +#endif + + +static +FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) +{ + FRESULT res = FR_OK; + + + if (sector != fs->winsect) { /* Window offset changed? */ +#if !_FS_READONLY + res = sync_window(fs); /* Write-back changes */ +#endif + if (res == FR_OK) { /* Fill sector window with new data */ + if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) { + sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */ + res = FR_DISK_ERR; + } + fs->winsect = sector; + } + } + return res; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Synchronize file system and strage device */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ + FATFS* fs /* File system object */ +) +{ + FRESULT res; + + + res = sync_window(fs); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { + /* Create FSInfo structure */ + mem_set(fs->win, 0, SS(fs)); + st_word(fs->win + BS_55AA, 0xAA55); + st_dword(fs->win + FSI_LeadSig, 0x41615252); + st_dword(fs->win + FSI_StrucSig, 0x61417272); + st_dword(fs->win + FSI_Free_Count, fs->free_clst); + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); + /* Write it into the FSInfo sector */ + fs->winsect = fs->volbase + 1; + disk_write(fs->drv, fs->win, fs->winsect, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR; + } + + return res; +} + +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clust2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= fs->n_fatent - 2) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ + _FDID* obj, /* Corresponding object */ + DWORD clst /* Cluster number to get the value */ +) +{ + UINT wc, bc; + DWORD val; + FATFS *fs = obj->fs; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ + val = 1; /* Internal error */ + + } else { + val = 0xFFFFFFFF; /* Default value falls on disk error */ + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc = fs->win[bc++ % SS(fs)]; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc |= fs->win[bc % SS(fs)] << 8; + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + break; + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; + val = ld_word(fs->win + clst * 2 % SS(fs)); + break; + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; + break; +#if _FS_EXFAT + case FS_EXFAT : + if (obj->objsize) { + DWORD cofs = clst - obj->sclust; /* Offset from start cluster */ + DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */ + + if (obj->stat == 2) { /* Is there no valid chain on the FAT? */ + if (cofs <= clen) { + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* Generate the value */ + break; + } + } + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the contiguous part? */ + val = clst + 1; /* Generate the value */ + break; + } + if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; + break; + } + } + /* go next */ +#endif + default: + val = 1; /* Internal error */ + } + } + + return val; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* Corresponding file system object */ + DWORD clst, /* FAT index number (cluster number) to be changed */ + DWORD val /* New value to be set to the entry */ +) +{ + UINT bc; + BYTE *p; + FRESULT res = FR_INT_ERR; + + + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ + switch (fs->fs_type) { + case FS_FAT12 : /* Bitfield items */ + bc = (UINT)clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc++ % SS(fs); + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc % SS(fs); + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + fs->wflag = 1; + break; + + case FS_FAT16 : /* WORD aligned items */ + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); + fs->wflag = 1; + break; + + case FS_FAT32 : /* DWORD aligned items */ +#if _FS_EXFAT + case FS_EXFAT : +#endif + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); + } + st_dword(fs->win + clst * 4 % SS(fs), val); + fs->wflag = 1; + break; + } + } + return res; +} + +#endif /* !_FS_READONLY */ + + + + +#if _FS_EXFAT && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* exFAT: Accessing FAT and Allocation Bitmap */ +/*-----------------------------------------------------------------------*/ + +/*---------------------------------------------*/ +/* exFAT: Find a contiguous free cluster block */ +/*---------------------------------------------*/ + +static +DWORD find_bitmap ( /* 0:No free cluster, 2..:Free cluster found, 0xFFFFFFFF:Disk error */ + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to scan from */ + DWORD ncl /* Number of contiguous clusters to find (1..) */ +) +{ + BYTE bm, bv; + UINT i; + DWORD val, scl, ctr; + + + clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */ + if (clst >= fs->n_fatent - 2) clst = 0; + scl = val = clst; ctr = 0; + for (;;) { + if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; /* (assuming bitmap is located top of the cluster heap) */ + i = val / 8 % SS(fs); bm = 1 << (val % 8); + do { + do { + bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ + if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ + val = 0; bm = 0; i = 4096; + } + if (!bv) { /* Is it a free cluster? */ + if (++ctr == ncl) return scl + 2; /* Check run length */ + } else { + scl = val; ctr = 0; /* Encountered a live cluster, restart to scan */ + } + if (val == clst) return 0; /* All cluster scanned? */ + } while (bm); + bm = 1; + } while (++i < SS(fs)); + } +} + + +/*------------------------------------*/ +/* exFAT: Set/Clear a block of bitmap */ +/*------------------------------------*/ + +static +FRESULT change_bitmap ( + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to change from */ + DWORD ncl, /* Number of clusters to be changed */ + int bv /* bit value to be set (0 or 1) */ +) +{ + BYTE bm; + UINT i; + DWORD sect; + + + clst -= 2; /* The first bit corresponds to cluster #2 */ + sect = fs->database + clst / 8 / SS(fs); /* Sector address (assuming bitmap is located top of the cluster heap) */ + i = clst / 8 % SS(fs); /* Byte offset in the sector */ + bm = 1 << (clst % 8); /* Bit mask in the byte */ + for (;;) { + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; + do { + do { + if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */ + fs->win[i] ^= bm; /* Flip the bit */ + fs->wflag = 1; + if (--ncl == 0) return FR_OK; /* All bits processed? */ + } while (bm <<= 1); /* Next bit */ + bm = 1; + } while (++i < SS(fs)); /* Next byte */ + i = 0; + } +} + + +/*---------------------------------------------*/ +/* Complement contiguous part of the FAT chain */ +/*---------------------------------------------*/ + +static +FRESULT fill_fat_chain ( + _FDID* obj /* Pointer to the corresponding object */ +) +{ + FRESULT res; + DWORD cl, n; + + if (obj->stat == 3) { /* Has the object been changed 'fragmented'? */ + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ + res = put_fat(obj->fs, cl, cl + 1); + if (res != FR_OK) return res; + } + obj->stat = 0; /* Change status 'FAT chain is valid' */ + } + return FR_OK; +} + +#endif /* _FS_EXFAT && !_FS_READONLY */ + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +static +FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ + _FDID* obj, /* Corresponding object */ + DWORD clst, /* Cluster to remove a chain from */ + DWORD pclst /* Previous cluster of clst (0:an entire chain) */ +) +{ + FRESULT res = FR_OK; + DWORD nxt; + FATFS *fs = obj->fs; +#if _FS_EXFAT || _USE_TRIM + DWORD scl = clst, ecl = clst; +#endif +#if _USE_TRIM + DWORD rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */ + + /* Mark the previous cluster 'EOC' on the FAT if it exists */ + if (pclst && (!_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) { + res = put_fat(fs, pclst, 0xFFFFFFFF); + if (res != FR_OK) return res; + } + + /* Remove the chain */ + do { + nxt = get_fat(obj, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ + if (res != FR_OK) return res; + } + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst++; + fs->fsi_flag |= 1; + } +#if _FS_EXFAT || _USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous cluster block */ +#if _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 _USE_TRIM + rt[0] = clust2sect(fs, scl); /* Start sector */ + rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Inform device the block can be erased */ +#endif + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } while (clst < fs->n_fatent); /* Repeat while not the last link */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + if (pclst == 0) { /* Does object have no chain? */ + obj->stat = 0; /* Change the object status 'initial' */ + } else { + if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Did the chain got contiguous? */ + obj->stat = 2; /* Change the object status 'contiguous' */ + } + } + } +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch a chain or Create a new chain */ +/*-----------------------------------------------------------------------*/ +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + _FDID* obj, /* Corresponding object */ + DWORD clst /* Cluster# to stretch, 0:Create a new chain */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + FATFS *fs = obj->fs; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clst; /* Get suggested cluster to start from */ + if (scl == 0 || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch current chain */ + cs = get_fat(obj, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Invalid value */ + if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; + } + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ + res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ + if (res == FR_INT_ERR) return 1; + if (res == FR_DISK_ERR) return 0xFFFFFFFF; + if (clst == 0) { /* Is it a new chain? */ + obj->stat = 2; /* Set status 'contiguous chain' */ + } else { /* This is a stretched chain */ + if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */ + obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */ + obj->stat = 3; /* Change status 'just fragmented' */ + } + } + } else +#endif + { /* On the FAT12/16/32 volume */ + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Check wrap-around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(obj, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* An error occurred */ + if (ncl == scl) return 0; /* No free cluster */ + } + } + + if (_FS_EXFAT && fs->fs_type == FS_EXFAT && obj->stat == 2) { /* Is it a contiguous chain? */ + res = FR_OK; /* FAT does not need to be written */ + } else { + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */ + if (res == FR_OK && clst) { + res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */ + } + } + + if (res == FR_OK) { /* Update FSINFO if function succeeded. */ + fs->last_clst = ncl; + if (fs->free_clst < fs->n_fatent - 2) fs->free_clst--; + fs->fsi_flag |= 1; + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Create error status */ + } + + return ncl; /* Return new cluster number or error status */ +} + +#endif /* !_FS_READONLY */ + + + + +#if _USE_FASTSEEK +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + FATFS *fs = fp->obj.fs; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (ncl == 0) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} + +#endif /* _USE_FASTSEEK */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to directory object */ + DWORD ofs /* Offset of directory table */ +) +{ + DWORD csz, clst; + FATFS *fs = dp->obj.fs; + + + if (ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */ + return FR_INT_ERR; + } + dp->dptr = ofs; /* Set current offset */ + clst = dp->obj.sclust; /* Table start cluster (0:root) */ + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */ + clst = fs->dirbase; + if (_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */ + } + + if (clst == 0) { /* Static table (root-directory in FAT12/16) */ + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */ + dp->sect = fs->dirbase; + + } else { /* Dynamic table (sub-directory or root-directory in FAT32+) */ + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ + while (ofs >= csz) { /* Follow cluster chain */ + clst = get_fat(&dp->obj, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */ + ofs -= csz; + } + dp->sect = clust2sect(fs, clst); + } + dp->clust = clst; /* Current cluster# */ + if (!dp->sect) return FR_INT_ERR; + dp->sect += ofs / SS(fs); /* Sector# of the directory entry */ + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ + DIR* dp, /* Pointer to the directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD ofs, clst; + FATFS *fs = dp->obj.fs; +#if !_FS_READONLY + UINT n; +#endif + + ofs = dp->dptr + SZDIRE; /* Next entry */ + if (!dp->sect || ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE; /* Report EOT when offset has reached max value */ + + if (ofs % SS(fs) == 0) { /* Sector changed? */ + dp->sect++; /* Next sector */ + + if (!dp->clust) { /* Static table */ + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */ + dp->sect = 0; return FR_NO_FILE; + } + } + else { /* Dynamic table */ + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst >= fs->n_fatent) { /* Reached end of dynamic table */ +#if !_FS_READONLY + if (!stretch) { /* If no stretch, report EOT */ + dp->sect = 0; return FR_NO_FILE; + } + clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + /* Clean-up the stretched table */ + if (_FS_EXFAT) dp->obj.stat |= 4; /* The directory needs to be updated */ + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ + mem_set(fs->win, 0, SS(fs)); /* Clear window buffer */ + for (n = 0, fs->winsect = clust2sect(fs, clst); n < fs->csize; n++, fs->winsect++) { /* Fill the new cluster with 0 */ + fs->wflag = 1; + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; + } + fs->winsect -= n; /* Restore window offset */ +#else + if (!stretch) dp->sect = 0; /* If no stretch, report EOT (this is to suppress warning) */ + dp->sect = 0; return FR_NO_FILE; /* Report EOT */ +#endif + } + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clust2sect(fs, clst); + } + } + } + dp->dptr = ofs; /* Current entry */ + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Directory handling - Reserve a block of directory entries */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to the directory object */ + UINT nent /* Number of contiguous entries to allocate */ +) +{ + FRESULT res; + UINT n; + FATFS *fs = dp->obj.fs; + + + res = dir_sdi(dp, 0); + if (res == FR_OK) { + n = 0; + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; +#if _FS_EXFAT + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { +#else + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { +#endif + if (++n == nent) break; /* A block of contiguous free entries is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dp, 1); + } while (res == FR_OK); /* Next entry with table stretch enabled */ + } + + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ + return res; +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT: Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static +DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ + FATFS* fs, /* Pointer to the fs object */ + const BYTE* dir /* Pointer to the key entry */ +) +{ + DWORD cl; + + cl = ld_word(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) { + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + } + + return cl; +} + + +#if !_FS_READONLY +static +void st_clust ( + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir, /* Pointer to the key entry */ + DWORD cl /* Value to be set */ +) +{ + st_word(dir + DIR_FstClusLO, (WORD)cl); + if (fs->fs_type == FS_FAT32) { + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + } +} +#endif + + + +#if _USE_LFN != 0 +/*------------------------------------------------------------------------*/ +/* FAT-LFN: LFN handling */ +/*------------------------------------------------------------------------*/ +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */ + + +/*--------------------------------------------------------*/ +/* FAT-LFN: Compare a part of file name with an LFN entry */ +/*--------------------------------------------------------*/ +static +int cmp_lfn ( /* 1:matched, 0:not matched */ + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ + BYTE* dir /* Pointer to the directory entry containing the part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { + if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ + return 0; /* Not matched */ + } + wc = uc; + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */ + + return 1; /* The part of LFN matched */ +} + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT +/*-----------------------------------------------------*/ +/* FAT-LFN: Pick a part of file name from an LFN entry */ +/*-----------------------------------------------------*/ +static +int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ + BYTE* dir /* Pointer to the LFN entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; /* The part of LFN is valid */ +} +#endif + + +#if !_FS_READONLY +/*-----------------------------------------*/ +/* FAT-LFN: Create an entry of LFN entries */ +/*-----------------------------------------*/ +static +void put_lfn ( + const WCHAR* lfn, /* Pointer to the LFN */ + BYTE* dir, /* Pointer to the LFN entry to be created */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* Checksum of the corresponding SFN */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set checksum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + st_word(dir + LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN working buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ + st_word(dir + LfnOfs[s], wc); /* Put it */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */ + } while (++s < 13); + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_LFN != 0 */ + + + +#if _USE_LFN != 0 && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Create a Numbered SFN */ +/*-----------------------------------------------------------------------*/ + +static +void gen_numname ( + BYTE* dst, /* Pointer to the buffer to store numbered SFN */ + const BYTE* src, /* Pointer to SFN */ + const WCHAR* lfn, /* Pointer to LFN */ + UINT seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + WCHAR wc; + DWORD sr; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ + sr = seq; + while (*lfn) { /* Create a CRC */ + wc = *lfn++; + for (i = 0; i < 16; i++) { + sr = (sr << 1) + (wc & 1); + wc >>= 1; + if (sr & 0x10000) sr ^= 0x11021; + } + } + seq = (UINT)sr; + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (BYTE)((seq % 16) + '0'); + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif /* _USE_LFN != 0 && !_FS_READONLY */ + + + +#if _USE_LFN != 0 +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Calculate checksum of an SFN entry */ +/*-----------------------------------------------------------------------*/ + +static +BYTE sum_sfn ( + const BYTE* dir /* Pointer to the SFN entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} + +#endif /* _USE_LFN != 0 */ + + + +#if _FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* exFAT: Checksum */ +/*-----------------------------------------------------------------------*/ + +static +WORD xdir_sum ( /* Get checksum of the directoly block */ + const BYTE* dir /* Directory entry block to be calculated */ +) +{ + UINT i, szblk; + WORD sum; + + + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; + for (i = sum = 0; i < szblk; i++) { + if (i == XDIR_SetSum) { /* Skip sum field */ + i++; + } else { + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; + } + } + return sum; +} + + + +static +WORD xname_sum ( /* Get check sum (to be used as hash) of the name */ + const WCHAR* name /* File name to be calculated */ +) +{ + WCHAR chr; + WORD sum = 0; + + + while ((chr = *name++) != 0) { + chr = ff_wtoupper(chr); /* File name needs to be ignored case */ + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); + } + return sum; +} + + +#if !_FS_READONLY && _USE_MKFS +static +DWORD xsum32 ( + BYTE dat, /* Data to be sumed */ + DWORD sum /* Previous value */ +) +{ + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; + return sum; +} +#endif + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +/*------------------------------------------------------*/ +/* exFAT: Get object information from a directory block */ +/*------------------------------------------------------*/ + +static +void get_xdir_info ( + BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ + FILINFO* fno /* Buffer to store the extracted file information */ +) +{ + UINT di, si; + WCHAR w; +#if !_LFN_UNICODE + UINT nc; +#endif + + /* Get file name */ +#if _LFN_UNICODE + if (dirb[XDIR_NumName] <= _MAX_LFN) { + for (si = SZDIRE * 2, di = 0; di < dirb[XDIR_NumName]; si += 2, di++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ld_word(dirb + si); /* Get a character */ + fno->fname[di] = w; /* Store it */ + } + } else { + di = 0; /* Buffer overflow and inaccessible object */ + } +#else + for (si = SZDIRE * 2, di = nc = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ld_word(dirb + si); /* Get a character */ + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) { di = 0; break; } /* Could not be converted and inaccessible object */ + if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */ + fno->fname[di++] = (char)(w >> 8); + } + if (di >= _MAX_LFN) { di = 0; break; } /* Buffer overflow and inaccessible object */ + fno->fname[di++] = (char)w; + } +#endif + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object? */ + fno->fname[di] = 0; /* Terminate file name */ + + fno->altname[0] = 0; /* No SFN */ + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ +} + +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + +/*-----------------------------------*/ +/* exFAT: Get a directry entry block */ +/*-----------------------------------*/ + +static +FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ + DIR* dp /* Pointer to the reading direcotry object pointing the 85 entry */ +) +{ + FRESULT res; + UINT i, nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + + + /* Load 85 entry */ + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR; + mem_cpy(dirb, dp->dir, SZDIRE); + nent = dirb[XDIR_NumSec] + 1; + + /* Load C0 entry */ + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR; + mem_cpy(dirb + SZDIRE, dp->dir, SZDIRE); + + /* Load C1 entries */ + if (nent < 3 || nent > 19) return FR_NO_FILE; + i = SZDIRE * 2; nent *= SZDIRE; + do { + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR; + mem_cpy(dirb + i, dp->dir, SZDIRE); + i += SZDIRE; + } while (i < nent); + + /* Sanity check */ + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; + + return FR_OK; +} + + +#if !_FS_READONLY || _FS_RPATH != 0 +/*------------------------------------------------*/ +/* exFAT: Load the object's directory entry block */ +/*------------------------------------------------*/ +static +FRESULT load_obj_dir ( + DIR* dp, /* Blank directory object to be used to access containing direcotry */ + const _FDID* obj /* Object with containing directory information */ +) +{ + FRESULT res; + + + /* Open object containing directory */ + dp->obj.fs = obj->fs; + dp->obj.sclust = obj->c_scl; + dp->obj.stat = (BYTE)obj->c_size; + dp->obj.objsize = obj->c_size & 0xFFFFFF00; + dp->blk_ofs = obj->c_ofs; + + res = dir_sdi(dp, dp->blk_ofs); /* Goto the block location */ + if (res == FR_OK) { + res = load_xdir(dp); /* Load the object's entry block */ + } + return res; +} +#endif + + +#if !_FS_READONLY +/*-----------------------------------------------*/ +/* exFAT: Store the directory block to the media */ +/*-----------------------------------------------*/ +static +FRESULT store_xdir ( + DIR* dp /* Pointer to the direcotry object */ +) +{ + FRESULT res; + UINT nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + + /* Create set sum */ + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); + nent = dirb[XDIR_NumSec] + 1; + + /* Store the set of directory to the volume */ + res = dir_sdi(dp, dp->blk_ofs); + while (res == FR_OK) { + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) break; + mem_cpy(dp->dir, dirb, SZDIRE); + dp->obj.fs->wflag = 1; + if (--nent == 0) break; + dirb += SZDIRE; + res = dir_next(dp, 0); + } + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; +} + + + +/*-------------------------------------------*/ +/* exFAT: Create a new directory enrty block */ +/*-------------------------------------------*/ + +static +void create_xdir ( + BYTE* dirb, /* Pointer to the direcotry entry block buffer */ + const WCHAR* lfn /* Pointer to the nul terminated file name */ +) +{ + UINT i; + BYTE nb, nc; + WCHAR chr; + + + mem_set(dirb, 0, 2 * SZDIRE); /* Initialize 85+C0 entry */ + dirb[XDIR_Type] = 0x85; + dirb[XDIR_Type + SZDIRE] = 0xC0; + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ + + i = SZDIRE * 2; /* C1 offset */ + nc = 0; nb = 1; chr = 1; + do { + dirb[i++] = 0xC1; dirb[i++] = 0; /* Entry type C1 */ + do { /* Fill name field */ + if (chr && (chr = lfn[nc]) != 0) nc++; /* Get a character if exist */ + st_word(dirb + i, chr); i += 2; /* Store it */ + } while (i % SZDIRE); + nb++; + } while (lfn[nc]); /* Fill next entry if any char follows */ + + dirb[XDIR_NumName] = nc; /* Set name length */ + dirb[XDIR_NumSec] = nb; /* Set number of C0+C1s */ +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_EXFAT */ + + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res = FR_NO_FILE; + FATFS *fs = dp->obj.fs; + BYTE a, c; +#if _USE_LFN != 0 + BYTE ord = 0xFF, sum = 0xFF; +#endif + + while (dp->sect) { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; /* Test for the entry type */ + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of the directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + if (_USE_LABEL && vol) { + if (c == 0x83) break; /* Volume label entry? */ + } else { + if (c == 0x85) { /* Start of the file entry block? */ + dp->blk_ofs = dp->dptr; /* Get location of the block */ + res = load_xdir(dp); /* Load the entry block */ + if (res == FR_OK) { + dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ + } + break; + } + } + } else +#endif + { /* On the FAT12/16/32 volume */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ +#if _USE_LFN != 0 /* LFN configuration */ + if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & LLEF) { /* Is it start of an LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; + dp->blk_ofs = dp->dptr; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + } + break; + } + } +#else /* Non LFN configuration */ + if (c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */ + break; + } +#endif + } + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ + return res; +} + +#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp /* Pointer to the directory object with the file name */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; + BYTE c; +#if _USE_LFN != 0 + BYTE a, ord, sum; +#endif + + res = dir_sdi(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + BYTE nc; + UINT di, ni; + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ + + while ((res = dir_read(dp, 0)) == FR_OK) { /* Read an item */ + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip the comparison if hash value mismatched */ + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ + if ((di % SZDIRE) == 0) di += 2; + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; + } + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ + } + return res; + } +#endif + /* On the FAT12/16/32 volume */ +#if _USE_LFN != 0 + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN != 0 /* LFN configuration */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; /* LFN start order */ + dp->blk_ofs = dp->dptr; /* Start offset of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } + } +#else /* Non LFN configuration */ + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ +#endif + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ + DIR* dp /* Target directory with object name to be created */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + UINT n, nlen, nent; + BYTE sn[12], sum; + + + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ + for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + DIR dj; + + nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res != FR_OK) return res; + dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */ + + if (dp->obj.sclust != 0 && (dp->obj.stat & 4)) { /* Has the sub-directory been stretched? */ + dp->obj.stat &= 3; + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */ + res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */ + if (res != FR_OK) return res; + res = load_obj_dir(&dj, &dp->obj); + if (res != FR_OK) return res; /* Load the object status */ + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */ + st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; + res = store_xdir(&dj); /* Store the object status */ + if (res != FR_OK) return res; + } + + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ + return FR_OK; + } +#endif + /* On the FAT12/16/32 volume */ + mem_cpy(sn, dp->fn, 12); + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */ + res = dir_find(dp); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + dp->fn[NSFLAG] = sn[NSFLAG]; + } + + /* Create an SFN with/without LFNs. */ + nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res == FR_OK && --nent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->dptr - nent * SZDIRE); + if (res == FR_OK) { + sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ + do { /* Store LFN entries in bottom first */ + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + fs->wflag = 1; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK && --nent); + } + } + +#else /* Non LFN configuration */ + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ + +#endif + + /* Set SFN entry */ + if (res == FR_OK) { + res = move_window(fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ +#if _USE_LFN != 0 + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + fs->wflag = 1; + } + } + + return res; +} + +#endif /* !_FS_READONLY */ + + + +#if !_FS_READONLY && _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ + DIR* dp /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + DWORD last = dp->dptr; + + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ + if (res == FR_OK) { + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + /* Mark an entry 'deleted' */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + dp->dir[XDIR_Type] &= 0x7F; + } else { /* On the FAT12/16/32 volume */ + dp->dir[DIR_Name] = DDEM; + } + fs->wflag = 1; + if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } +#else /* Non LFN configuration */ + + res = move_window(fs, dp->sect); + if (res == FR_OK) { + dp->dir[DIR_Name] = DDEM; + fs->wflag = 1; + } +#endif + + return res; +} + +#endif /* !_FS_READONLY && _FS_MINIMIZE == 0 */ + + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ + +static +void get_fileinfo ( /* No return code */ + DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ +) +{ + UINT i, j; + TCHAR c; + DWORD tm; +#if _USE_LFN != 0 + WCHAR w, lfv; + FATFS *fs = dp->obj.fs; +#endif + + + fno->fname[0] = 0; /* Invaidate file info */ + if (!dp->sect) return; /* Exit if read pointer has reached end of directory */ + +#if _USE_LFN != 0 /* LFN configuration */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + get_xdir_info(fs->dirbuf, fno); + return; + } else +#endif + { /* On the FAT12/16/32 volume */ + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ + i = j = 0; + while ((w = fs->lfnbuf[j++]) != 0) { /* Get an LFN character */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) { i = 0; break; } /* No LFN if it could not be converted */ + if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */ + fno->fname[i++] = (char)(w >> 8); + } +#endif + if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ + fno->fname[i++] = (TCHAR)w; + } + fno->fname[i] = 0; /* Terminate the LFN */ + } + } + + i = j = 0; + lfv = fno->fname[i]; /* LFN is exist if non-zero */ + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) { /* Insert a . if extension is exist */ + if (!lfv) fno->fname[j] = '.'; + fno->altname[j++] = '.'; + } +#if _LFN_UNICODE + if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dp->dir[i])) { + c = c << 8 | dp->dir[i++]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif + fno->altname[j] = c; + if (!lfv) { + if (IsUpper(c) && (dp->dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) { + c += 0x20; /* To lower */ + } + fno->fname[j] = c; + } + j++; + } + if (!lfv) { + fno->fname[j] = 0; + if (!dp->dir[DIR_NTres]) j = 0; /* Altname is no longer needed if neither LFN nor case info is exist. */ + } + fno->altname[j] = 0; /* Terminate the SFN */ + +#else /* Non-LFN configuration */ + i = j = 0; + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) fno->fname[j++] = '.'; /* Insert a . if extension is exist */ + fno->fname[j++] = c; + } + fno->fname[j] = 0; +#endif + + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ + tm = ld_dword(dp->dir + DIR_ModTime); /* Timestamp */ + fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16); +} + +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + + +#if _USE_FIND && _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Pattern matching */ +/*-----------------------------------------------------------------------*/ + +static +WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ + const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ +) +{ +#if !_LFN_UNICODE + WCHAR chr; + + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ +#ifdef _EXCVT + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#else + if (IsDBCS1(chr) && IsDBCS2(**ptr)) { /* Get DBC 2nd byte if needed */ + chr = chr << 8 | (BYTE)*(*ptr)++; + } +#endif + return chr; +#else + return ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ +#endif +} + + +static +int pattern_matching ( /* 0:not matched, 1:matched */ + const TCHAR* pat, /* Matching pattern */ + const TCHAR* nam, /* String to be tested */ + int skip, /* Number of pre-skip chars (number of ?s) */ + int inf /* Infinite search (* specified) */ +) +{ + const TCHAR *pp, *np; + WCHAR pc, nc; + int nm, nx; + + + while (skip--) { /* Pre-skip name chars */ + if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + } + if (!*pat && inf) return 1; /* (short circuit) */ + + do { + pp = pat; np = nam; /* Top of pattern and name to match */ + for (;;) { + if (*pp == '?' || *pp == '*') { /* Wildcard? */ + nm = nx = 0; + do { /* Analyze the wildcard chars */ + if (*pp++ == '?') nm++; else nx = 1; + } while (*pp == '?' || *pp == '*'); + if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ + nc = *np; break; /* Branch mismatched */ + } + pc = get_achar(&pp); /* Get a pattern char */ + nc = get_achar(&np); /* Get a name char */ + if (pc != nc) break; /* Branch mismatched? */ + if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */ + } + get_achar(&nam); /* nam++ */ + } while (inf && nc); /* Retry until end of name if infinite search is specified */ + + return 0; +} + +#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ + + + +/*-----------------------------------------------------------------------*/ +/* Pick a top segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ + DIR* dp, /* Pointer to the directory object */ + const TCHAR** path /* Pointer to pointer to the segment in the path string */ +) +{ +#if _USE_LFN != 0 /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *p; + + /* Create LFN in Unicode */ + p = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ') break; /* Break if end of the path name */ + if (w == '/' || w == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (di >= _MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + w = (w << 8) + b; /* Create a DBC */ + if (!IsDBCS2(b)) return FR_INVALID_NAME; /* Reject invalid sequence */ + } + w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ + lfn[di++] = w; /* Store the Unicode character */ + } + *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ +#if _FS_RPATH != 0 + if ((di == 1 && lfn[di - 1] == '.') || + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ + lfn[di] = 0; + for (i = 0; i < 11; i++) /* Create dot name for SFN entry */ + dp->fn[i] = (i < di) ? '.' : ' '; + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Snip off trailing spaces and dots if exist */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + lfn[di] = 0; /* LFN is created */ + if (di == 0) return FR_INVALID_NAME; /* Reject nul name */ + + /* Create SFN in directory form */ + mem_set(dp->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + i = b = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN character */ + if (!w) break; /* Break on end of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII character */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = ExCvt[w - 0x80]; /* Convert extended character to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Is this DBC? (always false at SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dp->fn[i++] = (BYTE)(w >> 8); + } else { /* SBC */ + if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dp->fn[i++] = (BYTE)w; + } + + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* Create LFN entry when there are composite capitals */ + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dp->fn[NSFLAG] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* _USE_LFN != 0 : Non-LFN configuration */ + BYTE c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + p = *path; sfn = dp->fn; + mem_set(sfn, ' ', 11); + si = i = 0; ni = 8; +#if _FS_RPATH != 0 + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = p + si; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; + if (c <= ' ') break; /* Break if end of the path name */ + if (c == '/' || c == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (c == '.' || i >= ni) { /* End of body or over size? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Over size or invalid dot */ + i = 8; ni = 11; /* Goto extension */ + continue; + } + if (c >= 0x80) { /* Extended character? */ +#ifdef _EXCVT + c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else +#if !_DF1S + return FR_INVALID_NAME; /* Reject extended characters (ASCII only cfg) */ +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false at SBCS cfg.) */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */ + sfn[i++] = c; + sfn[i++] = d; + } else { /* SBC */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (IsLower(c)) c -= 0x20; /* To upper */ + sfn[i++] = c; + } + } + *path = p + si; /* Return pointer to the next segment */ + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ + + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + + return FR_OK; +#endif /* _USE_LFN != 0 */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR* dp, /* Directory object to return last directory and found object */ + const TCHAR* path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE ns; + _FDID *obj = &dp->obj; + FATFS *fs = obj->fs; + + +#if _FS_RPATH != 0 + if (*path != '/' && *path != '\\') { /* Without heading separator */ + obj->sclust = fs->cdir; /* Start from the current directory */ + } else +#endif + { /* With heading separator */ + while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ + obj->sclust = 0; /* Start from the root directory */ + } +#if _FS_EXFAT && _FS_RPATH != 0 + if (fs->fs_type == FS_EXFAT && obj->sclust) { /* Retrieve the sub-directory status if needed */ + DIR dj; + + obj->c_scl = fs->cdc_scl; + obj->c_size = fs->cdc_size; + obj->c_ofs = fs->cdc_ofs; + res = load_obj_dir(&dj, obj); + if (res != FR_OK) return res; + obj->objsize = ld_dword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + } +#endif + + if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + dp->fn[NSFLAG] = NS_NONAME; + res = dir_sdi(dp, 0); + + } else { /* Follow path */ + for (;;) { + res = create_name(dp, &path); /* Get a segment name of the path */ + if (res != FR_OK) break; + res = dir_find(dp); /* Find an object with the segment name */ + ns = dp->fn[NSFLAG]; + if (res != FR_OK) { /* Failed to find the object */ + if (res == FR_NO_FILE) { /* Object is not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ + if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ + dp->fn[NSFLAG] = NS_NONAME; + res = FR_OK; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ + } + } + break; + } + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + /* Get into the sub-directory */ + if (!(obj->attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + res = FR_NO_PATH; break; + } +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + obj->c_scl = obj->sclust; /* Save containing directory information for next dir */ + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Open next directory */ + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + } else +#endif + { + obj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ + } + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get logical drive number from path name */ +/*-----------------------------------------------------------------------*/ + +static +int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ + const TCHAR** path /* Pointer to pointer to the path name */ +) +{ + const TCHAR *tp, *tt; + UINT i; + int vol = -1; +#if _STR_VOLUME_ID /* Find string drive id */ + static const char* const str[] = {_VOLUME_STRS}; + const char *sp; + char c; + TCHAR tc; +#endif + + + if (*path) { /* If the pointer is not a null */ + for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find ':' in the path */ + if (*tt == ':') { /* If a ':' is exist in the path name */ + tp = *path; + i = *tp++ - '0'; + if (i < 10 && tp == tt) { /* Is there a numeric drive id? */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = ++tt; + } + } +#if _STR_VOLUME_ID + else { /* No numeric drive number, find string drive id */ + i = 0; tt++; + do { + sp = str[i]; tp = *path; + do { /* Compare a string drive id with path name */ + c = *sp++; tc = *tp++; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || tp != tt) && ++i < _VOLUMES); /* Repeat for each id until pattern match */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = tt; + } + } +#endif + return vol; + } +#if _FS_RPATH != 0 && _VOLUMES >= 2 + vol = CurrVol; /* Current drive */ +#else + vol = 0; /* Drive 0 */ +#endif + } + return vol; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT boot sector */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */ + FATFS* fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT-VBR or not */ +) +{ + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ + + if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ + + if (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) { + if ((ld_dword(fs->win + BS_FilSysType) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */ + if (ld_dword(fs->win + BS_FilSysType32) == 0x33544146) return 0; /* Check "FAT3" string */ + } +#if _FS_EXFAT + if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; +#endif + return 2; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Find logical drive and check if the volume is mounted */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ + const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + FATFS** rfs, /* Pointer to pointer to the found file system object */ + BYTE mode /* !=0: Check write protection for write access */ +) +{ + BYTE fmt, *pt; + int vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; + WORD nrsv; + FATFS *fs; + UINT i; + + + /* Get logical drive number */ + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) return FR_INVALID_DRIVE; + + /* Check if the file system object is valid or not */ + fs = FatFs[vol]; /* Get pointer to the file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock the volume */ + *rfs = fs; /* Return pointer to the file system object */ + + mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */ + if (fs->fs_type) { /* If the volume has been mounted */ + stat = disk_status(fs->drv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ + if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ + return FR_WRITE_PROTECTED; + } + return FR_OK; /* The file system object is valid */ + } + } + + /* The file system object is not valid. */ + /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drv); /* Initialize the physical drive */ + if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + } + if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; + } +#if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; + if (SS(fs) > _MAX_SS || SS(fs) < _MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; +#endif + /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ + bsect = 0; + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */ + if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */ + for (i = 0; i < 4; i++) { /* Get partition offset */ + pt = fs->win + (MBR_Table + i * SZ_PTE); + br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0; + } + i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ + if (i) i--; + do { /* Find an FAT volume */ + bsect = br[i]; + fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */ + } while (!LD2PT(vol) && fmt >= 2 && ++i < 4); + } + if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* An FAT volume is found. Following code initializes the file system object */ + +#if _FS_EXFAT + if (fmt == 1) { + QWORD maxlba; + + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; + + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT revision (Must be 1.0) */ + + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ + if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ + + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ + + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ + + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ + + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ + fs->n_fatent = nclst + 2; + + /* Boundaries and Limits */ + fs->volbase = bsect; + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); + + /* Check if bitmap location is in assumption (at the first cluster) */ + if (move_window(fs, clust2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR; + for (i = 0; i < SS(fs); i += SZDIRE) { + if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */ + } + if (i == SS(fs)) return FR_NO_FILESYSTEM; +#if !_FS_READONLY + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ +#endif + fmt = FS_EXFAT; /* FAT sub-type */ + } else +#endif /* _FS_EXFAT */ + { + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ + + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ + if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ + + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); + + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT32; + if (nclst <= MAX_FAT16) fmt = FS_FAT16; + if (nclst <= MAX_FAT12) fmt = FS_FAT12; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data start sector */ + if (fmt == FS_FAT32) { + if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM;/* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ + +#if !_FS_READONLY + /* Get FSINFO if available */ + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ + fs->fsi_flag = 0x80; +#if (_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo32 == 1 */ + && ld_word(fs->win + BPB_FSInfo32) == 1 + && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (_FS_NOFSINFO & 1) == 0 + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); +#endif +#if (_FS_NOFSINFO & 2) == 0 + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif /* (_FS_NOFSINFO & 3) != 3 */ +#endif /* !_FS_READONLY */ + } + + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ +#if _USE_LFN == 1 + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ +#if _FS_EXFAT + fs->dirbuf = DirBuf; /* Static directory block working buuffer */ +#endif +#endif +#if _FS_RPATH != 0 + fs->cdir = 0; /* Initialize current directory */ +#endif +#if _FS_LOCK != 0 /* Clear file lock semaphores */ + clear_lock(fs); +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/directory object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ + _FDID* obj, /* Pointer to the _OBJ, the 1st member in the FIL/DIR object, to check validity */ + FATFS** fs /* Pointer to pointer to the owner file system object to return */ +) +{ + FRESULT res; + + + if (!obj || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || (disk_status(obj->fs->drv) & STA_NOINIT)) { + *fs = 0; /* The object is invalid */ + res = FR_INVALID_OBJECT; + } else { + *fs = obj->fs; /* Owner file sytem object */ + ENTER_FF(obj->fs); /* Lock file system */ + res = FR_OK; + } + return res; +} + + + + +/*--------------------------------------------------------------------------- + + Public Functions (FatFs API) + +----------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/ + const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ +) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + + /* Get logical drive number */ + vol = get_ldnumber(&rp); + if (vol < 0) return FR_INVALID_DRIVE; + cfs = FatFs[vol]; /* Pointer to fs object */ + + if (cfs) { +#if _FS_LOCK != 0 + clear_lock(cfs); +#endif +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; +#endif + cfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */ + + res = find_volume(&path, &fs, 0); /* Force mounted the volume */ + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL* fp, /* Pointer to the blank file object */ + const TCHAR* path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; +#if !_FS_READONLY + DWORD dw, cl, bcs, clst, sc; + FSIZE_t ofs; +#endif + DEF_NAMBUF + + + if (!fp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND; + res = find_volume(&path, &fs, mode); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ + res = FR_INVALID_NAME; + } +#if _FS_LOCK != 0 + else { + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + } +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ +#if _FS_LOCK != 0 + res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + mode |= FA_CREATE_ALWAYS; /* File is created */ + } + else { /* Any object is already existing */ + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = GET_FATTIME(); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + /* Get current allocation info */ + fp->obj.fs = fs; + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_CrtTime, dw); /* Set created time */ + fs->dirbuf[XDIR_CrtTime10] = 0; + st_dword(fs->dirbuf + XDIR_ModTime, dw); /* Set modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + fs->dirbuf[XDIR_Attr] = AM_ARC; /* Reset attribute */ + st_dword(fs->dirbuf + XDIR_FstClus, 0); /* Reset file allocation info */ + st_qword(fs->dirbuf + XDIR_FileSize, 0); + st_qword(fs->dirbuf + XDIR_ValidFileSize, 0); + fs->dirbuf[XDIR_GenFlags] = 1; + res = store_xdir(&dj); + if (res == FR_OK && fp->obj.sclust) { /* Remove the cluster chain if exist */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ + } + } else +#endif + { + /* Clean directory info */ + st_dword(dj.dir + DIR_CrtTime, dw); /* Set created time */ + st_dword(dj.dir + DIR_ModTime, dw); /* Set modified time */ + dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ + cl = ld_clust(fs, dj.dir); /* Get cluster chain */ + st_clust(fs, dj.dir, 0); /* Reset file allocation info */ + st_dword(dj.dir + DIR_FileSize, 0); + fs->wflag = 1; + + if (cl) { /* Remove the cluster chain if exist */ + dw = fs->winsect; + res = remove_chain(&dj.obj, cl, 0); + if (res == FR_OK) { + res = move_window(fs, dw); + fs->last_clst = cl - 1; /* Reuse the cluster hole */ + } + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Following succeeded */ + if (dj.obj.attr & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* R/O violation */ + res = FR_DENIED; + } + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA_MODIFIED; + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#if _FS_LOCK != 0 + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->obj.lockid) res = FR_INT_ERR; +#endif + } +#else /* R/O configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ + res = FR_INVALID_NAME; + } else { + if (dj.obj.attr & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } + } + } +#endif + + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get allocation info */ + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + fp->obj.c_scl = dj.obj.sclust; + fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fp->obj.c_ofs = dj.blk_ofs; + } else +#endif + { + fp->obj.sclust = ld_clust(fs, dj.dir); /* Get allocation info */ + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); + } +#if _USE_FASTSEEK + fp->cltbl = 0; /* Disable fast seek mode */ +#endif + fp->obj.fs = fs; /* Validate the file object */ + fp->obj.id = fs->id; + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ +#if !_FS_READONLY +#if !_FS_TINY + mem_set(fp->buf, 0, _MAX_SS); /* Clear sector buffer */ +#endif + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ + fp->fptr = fp->obj.objsize; /* Offset to seek */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ + clst = fp->obj.sclust; /* Follow the cluster chain */ + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ + if ((sc = clust2sect(fs, clst)) == 0) { + res = FR_INT_ERR; + } else { + fp->sect = sc + (DWORD)(ofs / SS(fs)); +#if !_FS_TINY + if (disk_read(fs->drv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; +#endif + } + } + } +#endif + } + + FREE_NAMBUF(); + } + + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL* fp, /* Pointer to the file object */ + void* buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, cc, csect; + BYTE *rbuff = (BYTE*)buff; + + + *br = 0; /* Clear read byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + remain = fp->obj.objsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data read */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow cluster chain from the origin */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */ + } + } + if (clst < 2) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + sect = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if _FS_TINY + if (fs->wflag && fs->winsect - sect < cc) { + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + } +#else + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + } +#endif +#endif + rcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY + if (fp->sect != sect) { /* Load data sector if not in cache */ +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ + } +#endif + fp->sect = sect; + } + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ +#if _FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#else + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#endif + } + + LEAVE_FF(fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL* fp, /* Pointer to the file object */ + const void* buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT* bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + UINT wcnt, cc, csect; + const BYTE *wbuff = (const BYTE*)buff; + + + *bw = 0; /* Clear write byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + /* Check fptr wrap-around (file size cannot reach 4GiB on FATxx) */ + if ((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); + } + + for ( ; btw; /* Repeat until all data written */ + wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow from the origin */ + if (clst == 0) { /* If no cluster is allocated, */ + clst = create_chain(&fp->obj, 0); /* create a new cluster chain */ + } + } else { /* On the middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ + } +#if _FS_TINY + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ +#else + if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + sect = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if _FS_MINIMIZE <= 2 +#if _FS_TINY + if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + fs->wflag = 0; + } +#else + if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif +#endif + wcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */ + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); + fs->winsect = sect; + } +#else + if (fp->sect != sect && /* Fill sector cache with file data */ + fp->fptr < fp->obj.objsize && + disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { + ABORT(fs, FR_DISK_ERR); + } +#endif + fp->sect = sect; + } + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ +#if _FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fs->wflag = 1; +#else + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fp->flag |= FA_DIRTY; +#endif + } + + fp->flag |= FA_MODIFIED; /* Set file change flag */ + + LEAVE_FF(fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD tm; + BYTE *dir; + DEF_NAMBUF + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { + if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ +#if !_FS_TINY + if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + /* Update the directory entry */ + tm = GET_FATTIME(); /* Modified time */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = fill_fat_chain(&fp->obj); /* Create FAT chain if needed */ + if (res == FR_OK) { + DIR dj; + + INIT_NAMBUF(fs); + res = load_obj_dir(&dj, &fp->obj); /* Load directory entry block */ + if (res == FR_OK) { + fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive bit */ + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation info */ + st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); + st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); + st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + st_dword(fs->dirbuf + XDIR_AccTime, 0); + res = store_xdir(&dj); /* Restore it to the directory */ + if (res == FR_OK) { + res = sync_fs(fs); + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + FREE_NAMBUF(); + } + } else +#endif + { + res = move_window(fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation info */ + st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ + st_dword(dir + DIR_ModTime, tm); /* Update modified time */ + st_word(dir + DIR_LstAccDate, 0); + fs->wflag = 1; + res = sync_fs(fs); /* Restore it to the directory */ + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL* fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + +#if !_FS_READONLY + res = f_sync(fp); /* Flush cached data */ + if (res == FR_OK) +#endif + { + res = validate(&fp->obj, &fs); /* Lock volume */ + if (res == FR_OK) { +#if _FS_LOCK != 0 + res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ + if (res == FR_OK) +#endif + { + fp->obj.fs = 0; /* Invalidate file object */ + } +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + } + return res; +} + + + + +#if _FS_RPATH >= 1 +/*-----------------------------------------------------------------------*/ +/* Change Current Directory or Current Drive, Get Current Directory */ +/*-----------------------------------------------------------------------*/ + +#if _VOLUMES >= 2 +FRESULT f_chdrive ( + const TCHAR* path /* Drive number */ +) +{ + int vol; + + + /* Get logical drive number */ + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + + CurrVol = (BYTE)vol; /* Set it as current volume */ + + return FR_OK; +} +#endif + + +FRESULT f_chdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { + fs->cdir = dj.obj.sclust; /* It is the start directory itself */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdc_scl = dj.obj.c_scl; + fs->cdc_size = dj.obj.c_size; + fs->cdc_ofs = dj.obj.c_ofs; + } +#endif + } else { + if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */ + fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */ + fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fs->cdc_ofs = dj.blk_ofs; + } else +#endif + { + fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */ + } + } else { + res = FR_NO_PATH; /* Reached but a file */ + } + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(fs, res); +} + + +#if _FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEF_NAMBUF + + + *buff = 0; + /* Get logical drive */ + res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + i = len; /* Bottom of buffer (directory stack base) */ + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ + dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */ + if (res != FR_OK) break; + res = move_window(fs, dj.sect); + if (res != FR_OK) break; + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = dir_read(&dj, 0); + if (res != FR_OK) break; + if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + for (n = 0; fno.fname[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = fno.fname[--n]; + buff[--i] = '/'; + } + } + tp = buff; + if (res == FR_OK) { +#if _VOLUMES >= 2 + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; +#endif + if (i == len) { /* Root-directory */ + *tp++ = '/'; + } else { /* Sub-directroy */ + do /* Add stacked path str */ + *tp++ = buff[i++]; + while (i < len); + } + } + *tp = 0; + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* _FS_RPATH >= 2 */ +#endif /* _FS_RPATH >= 1 */ + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File pointer from top of file */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, bcs, nsect; + FSIZE_t ifptr; +#if _USE_FASTSEEK + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; +#endif + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ +#if _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->obj.sclust; /* Origin of the chain */ + if (cl) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(&fp->obj, cl); + if (cl <= 1) ABORT(fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) { + *tbl = 0; /* Terminate table */ + } else { + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + } + } else { /* Fast seek */ + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */ + fp->fptr = ofs; /* Set file pointer */ + if (ofs) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clust2sect(fs, fp->clust); + if (!dsc) ABORT(fs, FR_INT_ERR); + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ +#endif + fp->sect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4GiB-1 if at FATxx */ +#endif + if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ + ofs = fp->obj.objsize; + } + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs) { + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->obj.sclust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(&fp->obj, 0); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->obj.sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ + ofs -= bcs; fp->fptr += bcs; +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + if (_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */ + if (clst == 0) { /* Clip file size in case of disk full */ + ofs = 0; break; + } + } else +#endif + { + clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ + } + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); + fp->clust = clst; + } + fp->fptr += ofs; + if (ofs % SS(fs)) { + nsect = clust2sect(fs, clst); /* Current sector */ + if (!nsect) ABORT(fs, FR_INT_ERR); + nsect += (DWORD)(ofs / SS(fs)); + } + } + } + if (!_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ +#endif + fp->sect = nsect; + } + } + + LEAVE_FF(fs, res); +} + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR* dp, /* Pointer to directory object to create */ + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS *fs; + _FDID *obj; + DEF_NAMBUF + + + if (!dp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + obj = &dp->obj; + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + obj->fs = fs; + INIT_NAMBUF(fs); + res = follow_path(dp, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ + if (obj->attr & AM_DIR) { /* This object is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + obj->c_scl = obj->sclust; /* Save containing directory inforamation */ + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object location and status */ + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + obj->sclust = ld_clust(fs, dp->dir); /* Get object location */ + } + } else { /* This object is a file */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + obj->id = fs->id; + res = dir_sdi(dp, 0); /* Rewind directory */ +#if _FS_LOCK != 0 + if (res == FR_OK) { + if (obj->sclust) { + obj->lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!obj->lockid) res = FR_TOO_MANY_OPEN_FILES; + } else { + obj->lockid = 0; /* Root directory need not to be locked */ + } + } +#endif + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + if (res != FR_OK) obj->fs = 0; /* Invalidate the directory object if function faild */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Close Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_closedir ( + DIR *dp /* Pointer to the directory object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + + + res = validate(&dp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { +#if _FS_LOCK != 0 + if (dp->obj.lockid) { /* Decrement sub-directory open counter */ + res = dec_lock(dp->obj.lockid); + } + if (res == FR_OK) +#endif + { + dp->obj.fs = 0; /* Invalidate directory object */ + } +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entries in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMBUF + + + res = validate(&dp->obj, &fs); /* Check validity of the directory object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dp, 0); /* Rewind the directory object */ + } else { + INIT_NAMBUF(fs); + res = dir_read(dp, 0); /* Read an item */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dp, fno); /* Get the object information */ + res = dir_next(dp, 0); /* Increment index for next */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */ + } + FREE_NAMBUF(); + } + } + LEAVE_FF(fs, res); +} + + + +#if _USE_FIND +/*-----------------------------------------------------------------------*/ +/* Find Next File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findnext ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to the file information structure */ +) +{ + FRESULT res; + + + for (;;) { + res = f_readdir(dp, fno); /* Get a directory item */ + if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ +#if _USE_LFN != 0 && _USE_FIND == 2 + if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ +#endif + } + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Find First File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findfirst ( + DIR* dp, /* Pointer to the blank directory object */ + FILINFO* fno, /* Pointer to the file information structure */ + const TCHAR* path, /* Pointer to the directory to open */ + const TCHAR* pattern /* Pointer to the matching pattern */ +) +{ + FRESULT res; + + + dp->pat = pattern; /* Save pointer to pattern string */ + res = f_opendir(dp, path); /* Open the target directory */ + if (res == FR_OK) { + res = f_findnext(dp, fno); /* Find the first item */ + } + return res; +} + +#endif /* _USE_FIND */ + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR* path, /* Pointer to the file path */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &dj.obj.fs, 0); + if (res == FR_OK) { + INIT_NAMBUF(dj.obj.fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ + res = FR_INVALID_NAME; + } else { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(dj.obj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR* path, /* Path name of the logical drive number */ + DWORD* nclst, /* Pointer to a variable to return number of free clusters */ + FATFS** fatfs /* Pointer to return pointer to corresponding file system object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD nfree, clst, sect, stat; + UINT i; + BYTE *p; + _FDID obj; + + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + *fatfs = fs; /* Return ptr to the fs object */ + /* If free_clst is valid, return it without full cluster scan */ + if (fs->free_clst <= fs->n_fatent - 2) { + *nclst = fs->free_clst; + } else { + /* Get number of free clusters */ + nfree = 0; + if (fs->fs_type == FS_FAT12) { /* FAT12: Sector unalighed FAT entries */ + clst = 2; obj.fs = fs; + do { + stat = get_fat(&obj, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) nfree++; + } while (++clst < fs->n_fatent); + } else { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan bitmap table */ + BYTE bm; + UINT b; + + clst = fs->n_fatent - 2; + sect = fs->database; + i = 0; + do { + if (i == 0 && (res = move_window(fs, sect++)) != FR_OK) break; + for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) { + if (!(bm & 1)) nfree++; + bm >>= 1; + } + i = (i + 1) % SS(fs); + } while (clst); + } else +#endif + { /* FAT16/32: Sector alighed FAT entries */ + clst = fs->n_fatent; sect = fs->fatbase; + i = 0; p = 0; + do { + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + p = fs->win; + i = SS(fs); + } + if (fs->fs_type == FS_FAT16) { + if (ld_word(p) == 0) nfree++; + p += 2; i -= 2; + } else { + if ((ld_dword(p) & 0x0FFFFFFF) == 0) nfree++; + p += 4; i -= 4; + } + } while (--clst); + } + } + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FSInfo is to be updated */ + } + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD ncl; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + if (fp->obj.objsize > fp->fptr) { + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fp->obj.sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(&fp->obj, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fs->n_fatent) { + res = remove_chain(&fp->obj, ncl, fp->clust); + } + } + fp->obj.objsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA_MODIFIED; +#if !_FS_TINY + if (res == FR_OK && (fp->flag & FA_DIRTY)) { + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fp->flag &= (BYTE)~FA_DIRTY; + } + } +#endif + if (res != FR_OK) ABORT(fs, res); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR* path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + DWORD dclst = 0; + FATFS *fs; +#if _FS_EXFAT + _FDID obj; +#endif + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; /* Cannot remove dot entry */ + } +#if _FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */ +#endif + if (res == FR_OK) { /* The object is accessible */ + if (dj.fn[NSFLAG] & NS_NONAME) { + res = FR_INVALID_NAME; /* Cannot remove the origin directory */ + } else { + if (dj.obj.attr & AM_RDO) { + res = FR_DENIED; /* Cannot remove R/O object */ + } + } + if (res == FR_OK) { +#if _FS_EXFAT + obj.fs = fs; + if (fs->fs_type == FS_EXFAT) { + obj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus); + obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + dclst = ld_clust(fs, dj.dir); + } + if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory ? */ +#if _FS_RPATH != 0 + if (dclst == fs->cdir) { /* Is it the current directory? */ + res = FR_DENIED; + } else +#endif + { + sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.sclust = dclst; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + sdj.obj.objsize = obj.objsize; + sdj.obj.stat = obj.stat; + } +#endif + res = dir_sdi(&sdj, 0); + if (res == FR_OK) { + res = dir_read(&sdj, 0); /* Read an item */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst) { /* Remove the cluster chain if exist */ +#if _FS_EXFAT + res = remove_chain(&obj, dclst, 0); +#else + res = remove_chain(&dj.obj, dclst, 0); +#endif + } + if (res == FR_OK) res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE *dir; + UINT n; + DWORD dsc, dcl, pcl, tm; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; + } + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(&dj.obj, 0); /* Allocate a cluster for the new directory table */ + dj.obj.objsize = (DWORD)fs->csize * SS(fs); + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) res = sync_window(fs); /* Flush FAT */ + tm = GET_FATTIME(); + if (res == FR_OK) { /* Initialize the new directory table */ + dsc = clust2sect(fs, dcl); + dir = fs->win; + mem_set(dir, 0, SS(fs)); + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + st_dword(dir + DIR_ModTime, tm); + st_clust(fs, dir, dcl); + mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */ + dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; + if (fs->fs_type == FS_FAT32 && pcl == fs->dirbase) pcl = 0; + st_clust(fs, dir + SZDIRE, pcl); + } + for (n = fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + fs->winsect = dsc++; + fs->wflag = 1; + res = sync_window(fs); + if (res != FR_OK) break; + mem_set(dir, 0, SS(fs)); + } + } + if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ + st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize); /* File size needs to be valid */ + st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize); + fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag (contiguous) */ + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ + res = store_xdir(&dj); + } else +#endif + { + dir = dj.dir; + st_dword(dir + DIR_ModTime, tm); /* Created time */ + st_clust(fs, dir, dcl); /* Table start cluster */ + dir[DIR_Attr] = AM_DIR; /* Attribute */ + fs->wflag = 1; + } + if (res == FR_OK) res = sync_fs(fs); + } else { + remove_chain(&dj.obj, dcl, 0); /* Could not register, remove cluster chain */ + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object name to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + FATFS *fs; + BYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir; + DWORD dw; + DEF_NAMBUF + + + get_ldnumber(&path_new); /* Ignore drive number of new name */ + res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */ + if (res == FR_OK) { + djo.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&djo, path_old); /* Check old object */ + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ +#if _FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&djo, 2); +#endif + if (res == FR_OK) { /* Object to be renamed is found */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At exFAT */ + BYTE nf, nn; + WORD nh; + + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + mem_cpy(&djn, &djo, sizeof djo); + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; + nh = ld_word(fs->dirbuf + XDIR_NameHash); + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); + fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; + st_word(fs->dirbuf + XDIR_NameHash, nh); +/* Start of critical section where any interruption can cause a cross-link */ + res = store_xdir(&djn); + } + } + } else +#endif + { /* At FAT12/FAT16/FAT32 */ + mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about the object except name */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy information about object except name */ + mem_cpy(dir + 13, buf + 2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clust2sect(fs, ld_clust(fs, dir)); + if (!dw) { + res = FR_INT_ERR; + } else { +/* Start of critical section where any interruption can cause a cross-link */ + res = move_window(fs, dw); + dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(fs, dir, djn.obj.sclust); + fs->wflag = 1; + } + } + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) { + res = sync_fs(fs); + } + } +/* End of critical section */ + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +#if _USE_CHMOD && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = store_xdir(&dj); + } else +#endif + { + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + fs->wflag = 1; + } + if (res == FR_OK) res = sync_fs(fs); + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR* path, /* Pointer to the file/directory name */ + const FILINFO* fno /* Pointer to the time stamp to be set */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + fs->wflag = 1; + } + if (res == FR_OK) res = sync_fs(fs); + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* _USE_CHMOD && !_FS_READONLY */ + + + +#if _USE_LABEL +/*-----------------------------------------------------------------------*/ +/* Get Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getlabel ( + const TCHAR* path, /* Path name of the logical drive number */ + TCHAR* label, /* Pointer to a buffer to return the volume label */ + DWORD* vsn /* Pointer to a variable to return the volume serial number */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT si, di; +#if _LFN_UNICODE || _FS_EXFAT + WCHAR w; +#endif + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + + /* Get volume label */ + if (res == FR_OK && label) { + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Find a volume label entry */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + for (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + w = ld_word(dj.dir + XDIR_Label + si * 2); +#if _LFN_UNICODE + label[di++] = w; +#else + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) w = '?'; /* Replace wrong character */ + if (_DF1S && w >= 0x100) label[di++] = (char)(w >> 8); + label[di++] = (char)w; +#endif + } + label[di] = 0; + } else +#endif + { + si = di = 0; /* Extract volume label from AM_VOL entry with code comversion */ + do { +#if _LFN_UNICODE + w = (si < 11) ? dj.dir[si++] : ' '; + if (IsDBCS1(w) && si < 11 && IsDBCS2(dj.dir[si])) { + w = w << 8 | dj.dir[si++]; + } + label[di++] = ff_convert(w, 1); /* OEM -> Unicode */ +#else + label[di++] = dj.dir[si++]; +#endif + } while (di < 11); + do { /* Truncate trailing spaces */ + label[di] = 0; + if (di == 0) break; + } while (label[--di] == ' '); + } + } + } + if (res == FR_NO_FILE) { /* No label entry and return nul string */ + label[0] = 0; + res = FR_OK; + } + } + + /* Get volume serial number */ + if (res == FR_OK && vsn) { + res = move_window(fs, fs->volbase); + if (res == FR_OK) { + switch (fs->fs_type) { + case FS_EXFAT: di = BPB_VolIDEx; break; + case FS_FAT32: di = BS_VolID32; break; + default: di = BS_VolID; + } + *vsn = ld_dword(fs->win + di); + } + } + + LEAVE_FF(fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Set Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setlabel ( + const TCHAR* label /* Pointer to the volume label to set */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE dirvn[22]; + UINT i, j, slen; + WCHAR w; + static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F"; + + + /* Get logical drive */ + res = find_volume(&label, &fs, FA_WRITE); + if (res != FR_OK) LEAVE_FF(fs, res); + dj.obj.fs = fs; + + /* Get length of given volume label */ + for (slen = 0; (UINT)label[slen] >= ' '; slen++) ; /* Get name length */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + for (i = j = 0; i < slen; ) { /* Create volume label in directory form */ + w = label[i++]; +#if !_LFN_UNICODE + if (IsDBCS1(w)) { + w = (i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } + w = ff_convert(w, 1); +#endif + if (w == 0 || chk_chr(badchr, w) || j == 22) { /* Check validity check validity of the volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + st_word(dirvn + j, w); j += 2; + } + slen = j; + } else +#endif + { /* On the FAT12/16/32 volume */ + for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */ + if (slen) { /* Is there a volume label to be set? */ + dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */ + do { +#if _LFN_UNICODE + w = ff_convert(ff_wtoupper(label[i++]), 0); +#else + w = (BYTE)label[i++]; + if (IsDBCS1(w)) { + w = (j < 10 && i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } +#if _USE_LFN != 0 + w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); +#else + if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ +#ifdef _EXCVT + if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else + if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ +#endif +#endif +#endif + if (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + if (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8); + dirvn[j++] = (BYTE)w; + } while (i < slen); + while (j < 11) dirvn[j++] = ' '; /* Fill remaining name field */ + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + } + } + + /* Set volume label */ + dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Get volume label entry */ + if (res == FR_OK) { + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); /* Change the volume label */ + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + } else { + if (slen) { + mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + } else { + dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ + } + } + fs->wflag = 1; + res = sync_fs(fs); + } else { /* No volume label entry is found or error */ + if (res == FR_NO_FILE) { + res = FR_OK; + if (slen) { /* Create a volume label entry */ + res = dir_alloc(&dj, 1); /* Allocate an entry */ + if (res == FR_OK) { + mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */ + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + } else { + dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ + mem_cpy(dj.dir, dirvn, 11); + } + fs->wflag = 1; + res = sync_fs(fs); + } + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_LABEL */ + + + +#if _USE_EXPAND && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Allocate a Contiguous Blocks to the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_expand ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t fsz, /* File size to be expanded to */ + BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, stcl, scl, ncl, tcl, lclst; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ +#endif + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ + stcl = fs->last_clst; lclst = 0; + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ + if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) { + if (opt) { + res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ + lclst = scl + tcl - 1; + } else { + lclst = scl - 1; + } + } + } else +#endif + { + scl = clst = stcl; ncl = 0; + for (;;) { /* Find a contiguous cluster block */ + n = get_fat(&fp->obj, clst); + if (++clst >= fs->n_fatent) clst = 2; + if (n == 1) { res = FR_INT_ERR; break; } + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (n == 0) { /* Is it a free cluster? */ + if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ + } else { + scl = clst; ncl = 0; /* Not a free cluster */ + } + if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ + } + if (res == FR_OK) { + if (opt) { + for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); + if (res != FR_OK) break; + lclst = clst; + } + } else { + lclst = scl - 1; + } + } + } + + if (res == FR_OK) { + fs->last_clst = lclst; /* Set suggested start cluster to start next */ + if (opt) { + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst -= tcl; + fs->fsi_flag |= 1; + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* _USE_EXPAND && !_FS_READONLY */ + + + +#if _USE_FORWARD +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_forward ( + FIL* fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btf, /* Number of bytes to forward */ + UINT* bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, csect; + BYTE *dbuf; + + + *bf = 0; /* Clear transfer byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + remain = fp->obj.objsize - fp->fptr; + if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ + + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */ + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + if (csect == 0) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->obj.sclust : get_fat(&fp->obj, fp->clust); + if (clst <= 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clust2sect(fs, fp->clust); /* Get current data sector */ + if (!sect) ABORT(fs, FR_INT_ERR); + sect += csect; +#if _FS_TINY + if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */ + dbuf = fs->win; +#else + if (fp->sect != sect) { /* Fill sector cache with file data */ +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + } + dbuf = fp->buf; +#endif + fp->sect = sect; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */ + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */ + if (!rcnt) ABORT(fs, FR_INT_ERR); + } + + LEAVE_FF(fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create FAT file system on the logical drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkfs ( + const TCHAR* path, /* Logical drive number */ + BYTE opt, /* Format option */ + DWORD au, /* Size of allocation unit [byte] */ + void* work, /* Pointer to working buffer */ + UINT len /* Size of working buffer */ +) +{ + const UINT n_fats = 1; /* Number of FATs for FAT12/16/32 volume (1 or 2) */ + const UINT n_rootdir = 512; /* Number of root directory entries for FAT12/16 volume */ + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4Ks unit) */ + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ + BYTE fmt, sys, *buf, *pte, pdrv, part; + WORD ss; + DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; + DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ + DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ + UINT i; + int vol; + DSTATUS stat; +#if _USE_TRIM || _FS_EXFAT + DWORD tbl[3]; +#endif + + + /* Check mounted drive and clear work area */ + vol = get_ldnumber(&path); /* Get target logical drive */ + if (vol < 0) return FR_INVALID_DRIVE; + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear mounted volume */ + pdrv = LD2PD(vol); /* Physical drive */ + part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */ + + /* Check physical drive status */ + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */ +#if _MAX_SS != _MIN_SS /* Get sector size of the medium */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; + if (ss > _MAX_SS || ss < _MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = _MAX_SS; +#endif + if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */ + au /= ss; /* Cluster size in unit of sector */ + + /* Get working buffer */ + buf = (BYTE*)work; /* Working buffer */ + sz_buf = len / ss; /* Size of working buffer (sector) */ + szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ + if (!szb_buf) return FR_MKFS_ABORTED; + + /* Determine where the volume to be located (b_vol, sz_vol) */ + if (_MULTI_PARTITION && part != 0) { + /* Get partition information from partition table in the MBR */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check if MBR is valid */ + pte = buf + (MBR_Table + (part - 1) * SZ_PTE); + if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */ + b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ + } else { + /* Create a single-partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR; + b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ + if (sz_vol < b_vol) return FR_MKFS_ABORTED; + sz_vol -= b_vol; /* Volume size */ + } + if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check if volume size is >=128s */ + + /* Pre-determine the FAT type */ + do { + if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ + if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */ + fmt = FS_EXFAT; break; + } + } + if (au > 128) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */ + if (opt & FM_FAT32) { /* FAT32 possible? */ + if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ + fmt = FS_FAT32; break; + } + } + if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER; /* no-FAT? */ + fmt = FS_FAT16; + } while (0); + +#if _FS_EXFAT + if (fmt == FS_EXFAT) { /* Create an exFAT volume */ + DWORD szb_bit, szb_case, sum, nb, cl; + WCHAR ch, si; + UINT j, st; + BYTE b; + + if (sz_vol < 0x1000) return FR_MKFS_ABORTED; /* Too small volume? */ +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Determine FAT location, data location and number of clusters */ + if (!au) { /* au auto-selection */ + au = 8; + if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */ + if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */ + } + b_fat = b_vol + 32; /* FAT start at offset 32 */ + sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ + b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ + if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED; /* Too small volume? */ + n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */ + if (n_clst <16) return FR_MKFS_ABORTED; /* Too few clusters? */ + if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED; /* Too many clusters? */ + + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */ + + /* Create a compressed up-case table */ + sect = b_data + au * tbl[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ + st = si = i = j = szb_case = 0; + do { + switch (st) { + case 0: + ch = ff_wtoupper(si); /* Get an up-case char */ + if (ch != si) { + si++; break; /* Store the up-case char if exist */ + } + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ + if (j >= 128) { + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ + } + st = 1; /* Do not compress short run */ + /* continue */ + case 1: + ch = si++; /* Fill the short run */ + if (--j == 0) st = 0; + break; + default: + ch = (WCHAR)j; si += j; /* Number of chars to skip */ + st = 0; + } + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); + i += 2; szb_case += 2; + if (!si || i == szb_buf) { /* Write buffered data when buffer full or end of process */ + n = (i + ss - 1) / ss; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; i = 0; + } + } while (si); + tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */ + tbl[2] = 1; /* Number of root dir clusters */ + + /* Initialize the allocation bitmap */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */ + nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ + do { + mem_set(buf, 0, szb_buf); + for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; + for (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the FAT */ + sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ + j = nb = cl = 0; + do { + mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */ + if (cl == 0) { /* Set entry 0 and 1 */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + } + do { /* Create chains of bitmap, up-case and root dir */ + while (nb && i < szb_buf) { /* Create a chain */ + st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); + i += 4; cl++; nb--; + } + if (!nb && j < 3) nb = tbl[j++]; /* Next chain */ + } while (nb && i < szb_buf); + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the root directory */ + mem_set(buf, 0, szb_buf); + buf[SZDIRE * 0 + 0] = 0x83; /* 83 entry (volume label) */ + buf[SZDIRE * 1 + 0] = 0x81; /* 81 entry (allocation bitmap) */ + st_dword(buf + SZDIRE * 1 + 20, 2); + st_dword(buf + SZDIRE * 1 + 24, szb_bit); + buf[SZDIRE * 2 + 0] = 0x82; /* 82 entry (up-case table) */ + st_dword(buf + SZDIRE * 2 + 4, sum); + st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); + st_dword(buf + SZDIRE * 2 + 24, szb_case); + sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */ + do { /* Fill root directory sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + + /* Create two set of the exFAT VBR blocks */ + sect = b_vol; + for (n = 0; n < 2; n++) { + /* Main record (+0) */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ + st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ + st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */ + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ + st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FSVerEx, 0x100); /* File system version (1.00) */ + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ + for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ + buf[BPB_NumFATsEx] = 1; /* Number of FATs */ + buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); + } + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + /* Extended bootstrap record (+1..+8) */ + mem_set(buf, 0, ss); + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + for (j = 1; j < 9; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* OEM/Reserved record (+9..+10) */ + mem_set(buf, 0, ss); + for ( ; j < 11; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* Sum record (+11) */ + for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + + } else +#endif /* _FS_EXFAT */ + { /* Create an FAT12/16/32 volume */ + do { + pau = au; + /* Pre-determine number of clusters and FAT sub-type */ + if (fmt == FS_FAT32) { /* FAT32 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x20000; /* Volume size in unit of 128KS */ + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; /* Number of clusters */ + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 32; /* Number of reserved sectors */ + sz_dir = 0; /* No static directory */ + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED; + } else { /* FAT12/16 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; + if (n_clst > MAX_FAT12) { + n = n_clst * 2 + 4; /* FAT size [byte] */ + } else { + fmt = FS_FAT12; + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ + } + sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 1; /* Number of reserved sectors */ + sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */ + } + b_fat = b_vol + sz_rsv; /* FAT base */ + b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */ + + /* Align data base to erase block boundary (for flash memory media) */ + n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */ + if (fmt == FS_FAT32) { /* FAT32: Move FAT base */ + sz_rsv += n; b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + sz_fat += n / n_fats; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; + if (fmt == FS_FAT32) { + if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ + if (!au && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT16) { + if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ + if (!au && (pau * 2) <= 64) { + au = pau * 2; continue; /* Adjust cluster size and retry */ + } + if ((opt & FM_FAT32)) { + fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ + } + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */ + + /* Ok, it is the valid cluster configuration */ + break; + } while (1); + +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Create FAT VBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ + st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ + if (sz_vol < 0x10000) { + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ + } else { + st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */ + } + buf[BPB_Media] = 0xF8; /* Media descriptor byte */ + st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ + if (fmt == FS_FAT32) { + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ + st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the VBR sector */ + + /* Create FSINFO record if needed */ + if (fmt == FS_FAT32) { + disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ + mem_set(buf, 0, ss); + st_dword(buf + FSI_LeadSig, 0x41615252); + st_dword(buf + FSI_StrucSig, 0x61417272); + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + st_word(buf + BS_55AA, 0xAA55); + disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ + disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ + } + + /* Initialize FAT area */ + mem_set(buf, 0, (UINT)szb_buf); + sect = b_fat; /* FAT start sector */ + for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ + if (fmt == FS_FAT32) { + st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ + st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */ + st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */ + } else { + st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */ + } + nsect = sz_fat; /* Number of FAT sectors */ + do { /* Fill FAT sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + } + + /* Initialize root directory (fill with zero) */ + nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ + do { + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + } + + /* Determine system ID in the partition table */ + if (_FS_EXFAT && fmt == FS_EXFAT) { + sys = 0x07; /* HPFS/NTFS/exFAT */ + } else { + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else { + if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (>=64KS) */ + } else { + sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 (<64KS) : FAT12 (<64KS) */ + } + } + } + + if (_MULTI_PARTITION && part != 0) { + /* Update system ID in the partition table */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Read the MBR */ + buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system type */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it back to the MBR */ + } else { + if (!(opt & FM_SFD)) { + /* Create partition table in FDISK format */ + mem_set(buf, 0, ss); + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ + pte[PTE_Boot] = 0; /* Boot indicator */ + pte[PTE_StHead] = 1; /* Start head */ + pte[PTE_StSec] = 1; /* Start sector */ + pte[PTE_StCyl] = 0; /* Start cylinder */ + pte[PTE_System] = sys; /* System type */ + n = (b_vol + sz_vol) / (63 * 255); /* (End CHS is incorrect) */ + pte[PTE_EdHead] = 254; /* End head */ + pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* End sector */ + pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ + st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ + st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */ + } + } + + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR; + + return FR_OK; +} + + + +#if _MULTI_PARTITION +/*-----------------------------------------------------------------------*/ +/* Create partition table on the physical drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_fdisk ( + BYTE pdrv, /* Physical drive number */ + const DWORD* szt, /* Pointer to the size table for each partitions */ + void* work /* Pointer to the working buffer */ +) +{ + UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; + BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; + DSTATUS stat; + DWORD sz_disk, sz_part, s_part; + + + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; + + /* Determine the CHS without any care of the drive geometry */ + for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; + if (n == 256) n--; + e_hd = n - 1; + sz_cyl = 63 * n; + tot_cyl = sz_disk / sz_cyl; + + /* Create partition table */ + mem_set(buf, 0, _MAX_SS); + p = buf + MBR_Table; b_cyl = 0; + for (i = 0; i < 4; i++, p += SZ_PTE) { + p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; + if (!p_cyl) continue; + s_part = (DWORD)sz_cyl * b_cyl; + sz_part = (DWORD)sz_cyl * p_cyl; + if (i == 0) { /* Exclude first track of cylinder 0 */ + s_hd = 1; + s_part += 63; sz_part -= 63; + } else { + s_hd = 0; + } + e_cyl = b_cyl + p_cyl - 1; + if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; + + /* Set partition table */ + p[1] = s_hd; /* Start head */ + p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ + p[3] = (BYTE)b_cyl; /* Start cylinder */ + p[4] = 0x06; /* System type (temporary setting) */ + p[5] = e_hd; /* End head */ + p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ + p[7] = (BYTE)e_cyl; /* End cylinder */ + st_dword(p + 8, s_part); /* Start sector in LBA */ + st_dword(p + 12, sz_part); /* Partition size */ + + /* Next partition */ + b_cyl += p_cyl; + } + st_word(p, 0xAA55); + + /* Write it to the MBR */ + return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; +} + +#endif /* _MULTI_PARTITION */ +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ + +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (characters) */ + FIL* fp /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read characters until buffer gets filled */ +#if _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Read a character in UTF-8 */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequence */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c & 0x1F) << 6 | (s[0] & 0x3F); + if (c < 0x80) c = '?'; + } else { + if (c < 0xF0) { /* Three-byte sequence */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); + if (c < 0x800) c = '?'; + } else { /* Reject four-byte sequence */ + c = '?'; + } + } + } +#elif _STRF_ENCODE == 2 /* Read a character in UTF-16BE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[1] + (s[0] << 8); +#elif _STRF_ENCODE == 1 /* Read a character in UTF-16LE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[0] + (s[1] << 8); +#else /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (IsDBCS1(c)) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c << 8) + s[0]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif +#else /* Read a character without conversion */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; +#endif + if (_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */ + *p++ = c; + n++; + if (c == '\n') break; /* Break on EOL */ + } + *p = 0; + return n ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ + +typedef struct { + FIL *fp; /* Ptr to the writing file */ + int idx, nchr; /* Write index of buf[] (-1:error), number of chars written */ + BYTE buf[64]; /* Write buffer */ +} putbuff; + + +static +void putc_bfd ( /* Buffered write with code conversion */ + putbuff* pb, + TCHAR c +) +{ + UINT bw; + int i; + + + if (_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */ + putc_bfd(pb, '\r'); + } + + i = pb->idx; /* Write index of pb->buf[] */ + if (i < 0) return; + +#if _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Write a character in UTF-8 */ + if (c < 0x80) { /* 7-bit */ + pb->buf[i++] = (BYTE)c; + } else { + if (c < 0x800) { /* 11-bit */ + pb->buf[i++] = (BYTE)(0xC0 | c >> 6); + } else { /* 16-bit */ + pb->buf[i++] = (BYTE)(0xE0 | c >> 12); + pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F)); + } +#elif _STRF_ENCODE == 2 /* Write a character in UTF-16BE */ + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#elif _STRF_ENCODE == 1 /* Write a character in UTF-16LE */ + pb->buf[i++] = (BYTE)c; + pb->buf[i++] = (BYTE)(c >> 8); +#else /* Write a character in ANSI/OEM */ + c = ff_convert(c, 0); /* Unicode -> OEM */ + if (!c) c = '?'; + if (c >= 0x100) + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#endif +#else /* Write a character without conversion */ + pb->buf[i++] = (BYTE)c; +#endif + + if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &bw); + i = (bw == (UINT)i) ? 0 : -1; + } + pb->idx = i; + pb->nchr++; +} + + +static +int putc_flush ( /* Flush left characters in the buffer */ + putbuff* pb +) +{ + UINT nw; + + if ( pb->idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK + && (UINT)pb->idx == nw) return pb->nchr; + return EOF; +} + + +static +void putc_init ( /* Initialize write buffer */ + putbuff* pb, + FIL* fp +) +{ + pb->fp = fp; + pb->nchr = pb->idx = 0; +} + + + +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + putc_bfd(&pb, c); /* Put the character */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + while (*str) putc_bfd(&pb, *str++); /* Put the string */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_printf ( + FIL* fp, /* Pointer to the file object */ + const TCHAR* fmt, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + putbuff pb; + BYTE f, r; + UINT i, j, w; + DWORD v; + TCHAR c, d, str[32], *p; + + + putc_init(&pb, fp); + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + while (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *fmt++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 4; c = *fmt++; + } + if (!c) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, ' '); + } + while (*p) putc_bfd(&pb, *p++); + while (j++ < w) putc_bfd(&pb, ' '); + continue; + case 'C' : /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; + case 'B' : /* Binary */ + r = 2; break; + case 'O' : /* Octal */ + r = 8; break; + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + case 'X' : /* Hexdecimal */ + r = 16; break; + default: /* Unknown type (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + str[i++] = d + '0'; + } while (v && i < sizeof str / sizeof str[0]); + if (f & 8) str[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + while (!(f & 2) && j++ < w) putc_bfd(&pb, d); + do putc_bfd(&pb, str[--i]); while (i); + while (j++ < w) putc_bfd(&pb, d); + } + + va_end(arp); + + return putc_flush(&pb); +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/components/fatfs/src/ff.h b/components/fatfs/src/ff.h new file mode 100644 index 0000000000..18bc85b1c7 --- /dev/null +++ b/components/fatfs/src/ff.h @@ -0,0 +1,369 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT file system module R0.12b / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2016, 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 _FATFS +#define _FATFS 68020 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONF +#error Wrong configuration file (ffconf.h). +#endif + +#ifdef FF_DEFINE_DIR +#define FF_DIR DIR +#endif + + +/* Definitions of volume management */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ +#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */ +#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */ + +#else /* Single partition configuration */ +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ +#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ + +#endif + + + +/* Type of path name strings on FatFs API */ + +#if _LFN_UNICODE /* Unicode (UTF-16) string */ +#if _USE_LFN == 0 +#error _LFN_UNICODE must be 0 at non-LFN cfg. +#endif +#ifndef _INC_TCHAR +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#endif +#else /* ANSI/OEM string */ +#ifndef _INC_TCHAR +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif +#endif + + + +/* Type of file size variables */ + +#if _FS_EXFAT +#if _USE_LFN == 0 +#error LFN must be enabled when enable exFAT +#endif +typedef QWORD FSIZE_t; +#else +typedef DWORD FSIZE_t; +#endif + + + +/* File system object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* File system type (0:N/A) */ + BYTE drv; /* Physical drive number */ + 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; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD csize; /* Cluster size [sectors] */ +#if _MAX_SS != _MIN_SS + WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ +#endif +#if _USE_LFN != 0 + WCHAR* lfnbuf; /* LFN working buffer */ +#endif +#if _FS_EXFAT + BYTE* dirbuf; /* Directory entry block scratchpad buffer */ +#endif +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !_FS_READONLY + DWORD last_clst; /* Last allocated cluster */ + DWORD free_clst; /* Number of free clusters */ +#endif +#if _FS_RPATH != 0 + DWORD cdir; /* Current directory start cluster (0:root) */ +#if _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] */ + DWORD volbase; /* Volume base sector */ + DWORD fatbase; /* FAT base sector */ + DWORD dirbase; /* Root directory base sector/cluster */ + DWORD database; /* Data base sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ +} FATFS; + + + +/* Object ID and allocation information (_FDID) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE attr; /* Object attribute */ + BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */ + DWORD sclust; /* Object start cluster (0:no cluster or root directory) */ + FSIZE_t objsize; /* Object size (valid when sclust != 0) */ +#if _FS_EXFAT + DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */ + 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 sclust != 0) */ +#endif +#if _FS_LOCK != 0 + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +} _FDID; + + + +/* File object structure (FIL) */ + +typedef struct { + _FDID 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 fprt is 0) */ + DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ +#if !_FS_READONLY + DWORD dir_sect; /* Sector number containing the directory entry */ + BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ +#endif +#if _USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS]; /* File private data read/write window */ +#endif +} FIL; + + + +/* Directory object structure (FF_DIR) */ + +typedef struct { + _FDID obj; /* Object identifier */ + DWORD dptr; /* Current read/write offset */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ + BYTE* dir; /* Pointer to the directory item in the win[] */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ +#if _USE_LFN != 0 + DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ +#endif +#if _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 _USE_LFN != 0 + TCHAR altname[13]; /* Altenative file name */ + TCHAR fname[_MAX_LFN + 1]; /* Primary file name */ +#else + TCHAR fname[13]; /* File name */ +#endif +} FILINFO; + + + +/* 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 > _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 szf, 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, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ +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) + +#ifndef EOF +#define EOF (-1) +#endif + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !_FS_READONLY && !_FS_NORTC +DWORD get_fattime (void); +#endif + +/* Unicode support functions */ +#if _USE_LFN != 0 /* Unicode - OEM code conversion */ +WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ +WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */ +#if _USE_LFN == 3 /* Memory functions */ +void* ff_memalloc (UINT msize); /* Allocate memory block */ +void ff_memfree (void* mblock); /* Free memory block */ +#endif +#endif + +/* Sync functions */ +#if _FS_REENTRANT +int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */ +int ff_req_grant (_SYNC_t sobj); /* Lock sync object */ +void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */ +int ff_del_syncobj (_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 /* _FATFS */ diff --git a/components/fatfs/src/ffconf.h b/components/fatfs/src/ffconf.h new file mode 100644 index 0000000000..23b63ea9e9 --- /dev/null +++ b/components/fatfs/src/ffconf.h @@ -0,0 +1,267 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file +/---------------------------------------------------------------------------*/ + +#define _FFCONF 68020 /* Revision ID */ + +/*---------------------------------------------------------------------------/ +/ Function Configurations +/---------------------------------------------------------------------------*/ + +#define _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 _FS_MINIMIZE 0 +/* This option defines minimization level to remove some basic API functions. +/ +/ 0: All basic functions are 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 _USE_STRFUNC 0 +/* This option switches string functions, f_gets(), f_putc(), f_puts() and +/ f_printf(). +/ +/ 0: Disable string functions. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. */ + + +#define _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 _USE_MKFS 1 +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ + + +#define _USE_FASTSEEK 0 +/* This option switches fast seek function. (0:Disable or 1:Enable) */ + + +#define _USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + + +#define _USE_CHMOD 0 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ + + +#define _USE_LABEL 0 +/* This option switches volume label functions, f_getlabel() and f_setlabel(). +/ (0:Disable or 1:Enable) */ + + +#define _USE_FORWARD 0 +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/---------------------------------------------------------------------------*/ + +#define _CODE_PAGE 1 +/* This option specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 1 - ASCII (No extended character. Non-LFN cfg. only) +/ 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) +*/ + + +#define _USE_LFN 0 +#define _MAX_LFN 255 +/* The _USE_LFN switches the support of long file name (LFN). +/ +/ 0: Disable support of LFN. _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, Unicode handling functions (option/unicode.c) must be added +/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and +/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255. +/ It should be set 255 to support full featured LFN operations. +/ 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(), must be added to the project. */ + + +#define _LFN_UNICODE 0 +/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) +/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. +/ This option also affects behavior of string I/O functions. */ + + +#define _STRF_ENCODE 3 +/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to +/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). +/ +/ 0: ANSI/OEM +/ 1: UTF-16LE +/ 2: UTF-16BE +/ 3: UTF-8 +/ +/ This option has no effect when _LFN_UNICODE == 0. */ + + +#define _FS_RPATH 0 +/* This option configures support of 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 _VOLUMES 2 +/* Number of volumes (logical drives) to be used. */ + + +#define _STR_VOLUME_ID 0 +#define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" +/* _STR_VOLUME_ID switches string support of volume ID. +/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive +/ number in the path name. _VOLUME_STRS defines the drive ID strings for each +/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for +/ the drive ID strings are: A-Z and 0-9. */ + + +#define _MULTI_PARTITION 1 +/* This option switches support of multi-partition on a 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 multi-partition 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. */ + + +#define _MIN_SS 512 +#define _MAX_SS 512 +/* These options configure the range of sector size to be supported. (512, 1024, +/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and +/ harddisk. But a larger value may be required for on-board flash memory and some +/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured +/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the +/ disk_ioctl() function. */ + + +#define _USE_TRIM 0 +/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the +/ disk_ioctl() function. */ + + +#define _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. +*/ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/---------------------------------------------------------------------------*/ + +#define _FS_TINY 0 +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the file system object (FATFS) is used for the file data transfer. */ + + +#define _FS_EXFAT 0 +/* This option switches support of exFAT file system. (0:Disable or 1:Enable) +/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) +/ Note that enabling exFAT discards C89 compatibility. */ + + +#define _FS_NORTC 0 +#define _NORTC_MON 1 +#define _NORTC_MDAY 1 +#define _NORTC_YEAR 2016 +/* The option _FS_NORTC switches timestamp functiton. If the system does not have +/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable +/ the timestamp function. All objects modified by FatFs will have a fixed timestamp +/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time. +/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to get current time form real-time clock. _NORTC_MON, +/ _NORTC_MDAY and _NORTC_YEAR have no effect. +/ These options have no effect at read-only configuration (_FS_READONLY = 1). */ + + +#define _FS_LOCK 0 +/* The option _FS_LOCK switches file lock function to control duplicated file open +/ and illegal operation to open objects. This option must be 0 when _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. */ + + +#define _FS_REENTRANT 1 +#define _FS_TIMEOUT 1000 +#define _SYNC_t SemaphoreHandle_t +/* The option _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. _FS_TIMEOUT and _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 _FS_TIMEOUT defines timeout period in unit of time tick. +/ The _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 "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +/*--- End of configuration options ---*/ diff --git a/components/fatfs/src/integer.h b/components/fatfs/src/integer.h new file mode 100644 index 0000000000..4660ed6244 --- /dev/null +++ b/components/fatfs/src/integer.h @@ -0,0 +1,38 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _FF_INTEGER +#define _FF_INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include +typedef unsigned __int64 QWORD; + + +#else /* Embedded platform */ + +/* These types MUST be 16-bit or 32-bit */ +typedef int INT; +typedef unsigned int UINT; + +/* This type MUST be 8-bit */ +typedef unsigned char BYTE; + +/* These types MUST be 16-bit */ +typedef short SHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types MUST be 32-bit */ +typedef long LONG; +typedef unsigned long DWORD; + +/* This type MUST be 64-bit (Remove this for C89 compatibility) */ +typedef unsigned long long QWORD; + +#endif + +#endif diff --git a/components/fatfs/src/option/syscall.c b/components/fatfs/src/option/syscall.c new file mode 100644 index 0000000000..b6ceeaa3fe --- /dev/null +++ b/components/fatfs/src/option/syscall.c @@ -0,0 +1,108 @@ +/*------------------------------------------------------------------------*/ +/* Sample code of OS dependent controls for FatFs */ +/* (C)ChaN, 2014 */ +/*------------------------------------------------------------------------*/ + + +#include "../ff.h" + + +#if _FS_REENTRANT +/*------------------------------------------------------------------------*/ +/* Create a Synchronization Object */ +/*------------------------------------------------------------------------*/ +/* This function is called in f_mount() function to create a new +/ synchronization object, 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) */ + _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 any error */ + _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 */ + _SYNC_t sobj /* Sync object to wait */ +) +{ + return (xSemaphoreTake(sobj, _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 ( + _SYNC_t sobj /* Sync object to be signaled */ +) +{ + xSemaphoreGive(sobj); +} + +#endif + + + + +#if _USE_LFN == 3 /* LFN with a working buffer on the heap */ +/*------------------------------------------------------------------------*/ +/* Allocate a memory block */ +/*------------------------------------------------------------------------*/ +/* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE. +*/ + +void* ff_memalloc ( /* Returns pointer to the allocated memory block */ + 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 */ +) +{ + free(mblock); /* Discard the memory block with POSIX API */ +} + +#endif diff --git a/components/fatfs/src/option/unicode.c b/components/fatfs/src/option/unicode.c new file mode 100644 index 0000000000..170e2e09e0 --- /dev/null +++ b/components/fatfs/src/option/unicode.c @@ -0,0 +1,17 @@ +#include "../ff.h" + +#if _USE_LFN != 0 + +#if _CODE_PAGE == 932 /* Japanese Shift_JIS */ +#include "cc932.c" +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#include "cc936.c" +#elif _CODE_PAGE == 949 /* Korean */ +#include "cc949.c" +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#include "cc950.c" +#else /* Single Byte Character-Set */ +#include "ccsbcs.c" +#endif + +#endif diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c new file mode 100644 index 0000000000..4ef387b43b --- /dev/null +++ b/components/fatfs/src/vfs_fat.c @@ -0,0 +1,538 @@ +// Copyright 2015-2016 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. + +#include +#include +#include +#include +#include +#include +#include +#include "esp_vfs.h" +#include "esp_log.h" +#include "ff.h" + +#include "diskio.h" + + +typedef struct { + char fat_drive[8]; + size_t max_files; + FATFS fs; + FIL files[0]; + _lock_t lock; +} vfs_fat_ctx_t; + +typedef struct { + DIR dir; + long offset; + FF_DIR ffdir; + FILINFO filinfo; + struct dirent cur_dirent; +} vfs_fat_dir_t; + + +static const char* TAG = "vfs_fat"; + +static size_t vfs_fat_write(void* p, int fd, const void * data, size_t size); +static off_t vfs_fat_lseek(void* p, int fd, off_t size, int mode); +static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size); +static int vfs_fat_open(void* ctx, const char * path, int flags, int mode); +static int vfs_fat_close(void* ctx, int fd); +static int vfs_fat_fstat(void* ctx, int fd, struct stat * st); +static int vfs_fat_stat(void* ctx, const char * path, struct stat * st); +static int vfs_fat_link(void* ctx, const char* n1, const char* n2); +static int vfs_fat_unlink(void* ctx, const char *path); +static int vfs_fat_rename(void* ctx, const char *src, const char *dst); +static DIR* vfs_fat_opendir(void* ctx, const char* name); +static struct dirent* vfs_fat_readdir(void* ctx, DIR* pdir); +static int vfs_fat_readdir_r(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent); +static long vfs_fat_telldir(void* ctx, DIR* pdir); +static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset); +static int vfs_fat_closedir(void* ctx, DIR* pdir); +static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode); +static int vfs_fat_rmdir(void* ctx, const char* name); + + +static char s_base_path[ESP_VFS_PATH_MAX]; +static vfs_fat_ctx_t* s_fat_ctx = NULL; + +esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, size_t max_files, FATFS** out_fs) +{ + if (s_fat_ctx) { + return ESP_ERR_INVALID_STATE; + } + const esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_CONTEXT_PTR, + .write_p = &vfs_fat_write, + .lseek_p = &vfs_fat_lseek, + .read_p = &vfs_fat_read, + .open_p = &vfs_fat_open, + .close_p = &vfs_fat_close, + .fstat_p = &vfs_fat_fstat, + .stat_p = &vfs_fat_stat, + .link_p = &vfs_fat_link, + .unlink_p = &vfs_fat_unlink, + .rename_p = &vfs_fat_rename, + .opendir_p = &vfs_fat_opendir, + .closedir_p = &vfs_fat_closedir, + .readdir_p = &vfs_fat_readdir, + .readdir_r_p = &vfs_fat_readdir_r, + .seekdir_p = &vfs_fat_seekdir, + .telldir_p = &vfs_fat_telldir, + .mkdir_p = &vfs_fat_mkdir, + .rmdir_p = &vfs_fat_rmdir + }; + size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL); + s_fat_ctx = (vfs_fat_ctx_t*) calloc(1, ctx_size); + if (s_fat_ctx == NULL) { + return ESP_ERR_NO_MEM; + } + s_fat_ctx->max_files = max_files; + strncpy(s_fat_ctx->fat_drive, fat_drive, sizeof(s_fat_ctx->fat_drive) - 1); + *out_fs = &s_fat_ctx->fs; + esp_err_t err = esp_vfs_register(base_path, &vfs, s_fat_ctx); + if (err != ESP_OK) { + free(s_fat_ctx); + s_fat_ctx = NULL; + return err; + } + _lock_init(&s_fat_ctx->lock); + strncpy(s_base_path, base_path, sizeof(s_base_path) - 1); + s_base_path[sizeof(s_base_path) - 1] = 0; + return ESP_OK; +} + +esp_err_t esp_vfs_fat_unregister() +{ + if (s_fat_ctx == NULL) { + return ESP_ERR_INVALID_STATE; + } + esp_err_t err = esp_vfs_unregister(s_base_path); + if (err != ESP_OK) { + return err; + } + _lock_close(&s_fat_ctx->lock); + free(s_fat_ctx); + s_fat_ctx = NULL; + return ESP_OK; +} + +static int get_next_fd(vfs_fat_ctx_t* fat_ctx) +{ + for (size_t i = 0; i < fat_ctx->max_files; ++i) { + if (fat_ctx->files[i].obj.fs == NULL) { + return (int) i; + } + } + return -1; +} + +static int fat_mode_conv(int m) +{ + int res = 0; + int acc_mode = m & O_ACCMODE; + if (acc_mode == O_RDONLY) { + res |= FA_READ; + } else if (acc_mode == O_WRONLY) { + res |= FA_WRITE; + } else if (acc_mode == O_RDWR) { + res |= FA_READ | FA_WRITE; + } + if ((m & O_CREAT) && (m & O_EXCL)) { + res |= FA_CREATE_NEW; + } else if (m & O_CREAT) { + res |= FA_CREATE_ALWAYS; + } else if (m & O_APPEND) { + res |= FA_OPEN_ALWAYS; + } else { + res |= FA_OPEN_EXISTING; + } + return res; +} + +static int fresult_to_errno(FRESULT fr) +{ + switch(fr) { + case FR_DISK_ERR: return EIO; + case FR_INT_ERR: + assert(0 && "fatfs internal error"); + return EIO; + case FR_NOT_READY: return ENODEV; + case FR_NO_FILE: return ENOENT; + case FR_NO_PATH: return ENOENT; + case FR_INVALID_NAME: return EINVAL; + case FR_DENIED: return EACCES; + case FR_EXIST: return EEXIST; + case FR_INVALID_OBJECT: return EBADF; + case FR_WRITE_PROTECTED: return EACCES; + case FR_INVALID_DRIVE: return ENXIO; + case FR_NOT_ENABLED: return ENODEV; + case FR_NO_FILESYSTEM: return ENODEV; + case FR_MKFS_ABORTED: return EINTR; + case FR_TIMEOUT: return ETIMEDOUT; + case FR_LOCKED: return EACCES; + case FR_NOT_ENOUGH_CORE: return ENOMEM; + case FR_TOO_MANY_OPEN_FILES: return ENFILE; + case FR_INVALID_PARAMETER: return EINVAL; + case FR_OK: return 0; + } + assert(0 && "unhandled FRESULT"); + return ENOTSUP; +} + +static void file_cleanup(vfs_fat_ctx_t* ctx, int fd) +{ + memset(&ctx->files[fd], 0, sizeof(FIL)); +} + +static int vfs_fat_open(void* ctx, const char * path, int flags, int mode) +{ + ESP_LOGV(TAG, "%s: path=\"%s\", flags=%x, mode=%x", __func__, path, flags, mode); + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + _lock_acquire(&s_fat_ctx->lock); + int fd = get_next_fd(fat_ctx); + if (fd < 0) { + ESP_LOGE(TAG, "open: no free file descriptors"); + errno = ENFILE; + fd = -1; + goto out; + } + FRESULT res = f_open(&fat_ctx->files[fd], path, fat_mode_conv(flags)); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + file_cleanup(fat_ctx, fd); + errno = fresult_to_errno(res); + fd = -1; + goto out; + } +out: + _lock_release(&s_fat_ctx->lock); + return fd; +} + +static size_t vfs_fat_write(void* ctx, int fd, const void * data, size_t size) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + FIL* file = &fat_ctx->files[fd]; + unsigned written = 0; + FRESULT res = f_write(file, data, size, &written); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + if (written == 0) { + return -1; + } + } + return written; +} + +static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + FIL* file = &fat_ctx->files[fd]; + unsigned read = 0; + FRESULT res = f_read(file, dst, size, &read); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + if (read == 0) { + return -1; + } + } + return read; +} + +static int vfs_fat_close(void* ctx, int fd) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + _lock_acquire(&s_fat_ctx->lock); + FIL* file = &fat_ctx->files[fd]; + FRESULT res = f_close(file); + file_cleanup(fat_ctx, fd); + int rc = 0; + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + rc = -1; + } + _lock_release(&s_fat_ctx->lock); + return rc; +} + +static off_t vfs_fat_lseek(void* ctx, int fd, off_t offset, int mode) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + FIL* file = &fat_ctx->files[fd]; + off_t new_pos; + if (mode == SEEK_SET) { + new_pos = offset; + } else if (mode == SEEK_CUR) { + off_t cur_pos = f_tell(file); + new_pos = cur_pos + offset; + } else if (mode == SEEK_END) { + off_t size = f_size(file); + new_pos = size + offset; + } else { + errno = EINVAL; + return -1; + } + FRESULT res = f_lseek(file, new_pos); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return new_pos; +} + +static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + FIL* file = &fat_ctx->files[fd]; + st->st_size = f_size(file); + st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; + return 0; +} + +static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) +{ + FILINFO info; + FRESULT res = f_stat(path, &info); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + st->st_size = info.fsize; + st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | + ((info.fattrib & AM_DIR) ? S_IFDIR : S_IFREG); + struct tm tm; + uint16_t fdate = info.fdate; + tm.tm_mday = fdate & 0x1f; + fdate >>= 5; + tm.tm_mon = (fdate & 0xf) - 1; + fdate >>=4; + tm.tm_year = fdate + 80; + uint16_t ftime = info.ftime; + tm.tm_sec = (ftime & 0x1f) * 2; + ftime >>= 5; + tm.tm_min = (ftime & 0x3f); + ftime >>= 6; + tm.tm_hour = (ftime & 0x1f); + st->st_mtime = mktime(&tm); + return 0; +} + +static int vfs_fat_unlink(void* ctx, const char *path) +{ + FRESULT res = f_unlink(path); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static int vfs_fat_link(void* ctx, const char* n1, const char* n2) +{ + const size_t copy_buf_size = 4096; + void* buf = malloc(copy_buf_size); + if (buf == NULL) { + errno = ENOMEM; + return -1; + } + FIL f1; + FRESULT res = f_open(&f1, n1, FA_READ | FA_OPEN_EXISTING); + if (res != FR_OK) { + goto fail1; + } + FIL f2; + res = f_open(&f2, n2, FA_WRITE | FA_CREATE_NEW); + if (res != FR_OK) { + goto fail2; + } + size_t size_left = f_size(&f1); + while (size_left > 0) { + size_t will_copy = (size_left < copy_buf_size) ? size_left : copy_buf_size; + size_t read; + res = f_read(&f1, buf, will_copy, &read); + if (res != FR_OK) { + goto fail3; + } else if (read != will_copy) { + res = FR_DISK_ERR; + goto fail3; + } + size_t written; + res = f_write(&f2, buf, will_copy, &written); + if (res != FR_OK) { + goto fail3; + } else if (written != will_copy) { + res = FR_DISK_ERR; + goto fail3; + } + size_left -= will_copy; + } + +fail3: + f_close(&f2); +fail2: + f_close(&f1); +fail1: + free(buf); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static int vfs_fat_rename(void* ctx, const char *src, const char *dst) +{ + FRESULT res = f_rename(src, dst); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static DIR* vfs_fat_opendir(void* ctx, const char* name) +{ + vfs_fat_dir_t* fat_dir = calloc(1, sizeof(vfs_fat_dir_t)); + if (!fat_dir) { + errno = ENOMEM; + return NULL; + } + FRESULT res = f_opendir(&fat_dir->ffdir, name); + if (res != FR_OK) { + free(fat_dir); + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return NULL; + } + return (DIR*) fat_dir; +} + +static int vfs_fat_closedir(void* ctx, DIR* pdir) +{ + assert(pdir); + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + FRESULT res = f_closedir(&fat_dir->ffdir); + free(pdir); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static struct dirent* vfs_fat_readdir(void* ctx, DIR* pdir) +{ + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + struct dirent* out_dirent; + int err = vfs_fat_readdir_r(ctx, pdir, &fat_dir->cur_dirent, &out_dirent); + if (err != 0) { + errno = err; + return NULL; + } + return out_dirent; +} + +static int vfs_fat_readdir_r(void* ctx, DIR* pdir, + struct dirent* entry, struct dirent** out_dirent) +{ + assert(pdir); + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + FRESULT res = f_readdir(&fat_dir->ffdir, &fat_dir->filinfo); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + return fresult_to_errno(res); + } + if (fat_dir->filinfo.fname[0] == 0) { + // end of directory + *out_dirent = NULL; + return 0; + } + entry->d_ino = 0; + if (fat_dir->filinfo.fattrib & AM_DIR) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + strlcpy(entry->d_name, fat_dir->filinfo.fname, + sizeof(entry->d_name)); + fat_dir->offset++; + *out_dirent = entry; + return 0; +} + +static long vfs_fat_telldir(void* ctx, DIR* pdir) +{ + assert(pdir); + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + return fat_dir->offset; +} + +static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset) +{ + assert(pdir); + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + FRESULT res; + if (offset < fat_dir->offset) { + res = f_rewinddir(&fat_dir->ffdir); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: rewinddir fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return; + } + fat_dir->offset = 0; + } + while (fat_dir->offset < offset) { + res = f_readdir(&fat_dir->ffdir, &fat_dir->filinfo); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: f_readdir fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return; + } + fat_dir->offset++; + } +} + +static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode) +{ + (void) mode; + FRESULT res = f_mkdir(name); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static int vfs_fat_rmdir(void* ctx, const char* name) +{ + FRESULT res = f_unlink(name); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} diff --git a/components/fatfs/src/vfs_fat_sdmmc.c b/components/fatfs/src/vfs_fat_sdmmc.c new file mode 100644 index 0000000000..e5956dd7b2 --- /dev/null +++ b/components/fatfs/src/vfs_fat_sdmmc.c @@ -0,0 +1,126 @@ +// Copyright 2015-2016 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. + +#include +#include "esp_log.h" +#include "esp_vfs.h" +#include "esp_vfs_fat.h" +#include "driver/sdmmc_host.h" +#include "sdmmc_cmd.h" +#include "diskio.h" + +static const char* TAG = "vfs_fat_sdmmc"; +static sdmmc_card_t* s_card = NULL; + +esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, + const sdmmc_host_t* host_config, + const sdmmc_slot_config_t* slot_config, + const esp_vfs_fat_sdmmc_mount_config_t* mount_config, + sdmmc_card_t** out_card) +{ + const size_t workbuf_size = 4096; + void* workbuf = NULL; + + if (s_card != NULL) { + return ESP_ERR_INVALID_STATE; + } + // enable SDMMC + sdmmc_host_init(); + + // enable card slot + sdmmc_host_init_slot(host_config->slot, slot_config); + s_card = malloc(sizeof(sdmmc_card_t)); + if (s_card == NULL) { + return ESP_ERR_NO_MEM; + } + + // probe and initialize card + esp_err_t err = sdmmc_card_init(host_config, s_card); + if (err != ESP_OK) { + ESP_LOGD(TAG, "sdmmc_card_init failed 0x(%x)", err); + goto fail; + } + if (out_card != NULL) { + *out_card = s_card; + } + + // connect SDMMC driver to FATFS + ff_diskio_register_sdmmc(0, s_card); + + // connect FATFS to VFS + FATFS* fs; + err = esp_vfs_fat_register(base_path, "", 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, "", 1); + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGW(TAG, "failed to mount card (%d)", res); + if (!(res == FR_NO_FILESYSTEM && mount_config->format_if_mount_failed)) { + goto fail; + } + ESP_LOGW(TAG, "partitioning card"); + DWORD plist[] = {100, 0, 0, 0}; + workbuf = malloc(workbuf_size); + res = f_fdisk(0, plist, workbuf); + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGD(TAG, "f_fdisk failed (%d)", res); + goto fail; + } + ESP_LOGW(TAG, "formatting card"); + res = f_mkfs("", FM_ANY, s_card->csd.sector_size, workbuf, workbuf_size); + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGD(TAG, "f_mkfs failed (%d)", res); + goto fail; + } + free(workbuf); + ESP_LOGW(TAG, "mounting again"); + res = f_mount(fs, "", 0); + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res); + goto fail; + } + } + return ESP_OK; + +fail: + free(workbuf); + esp_vfs_unregister(base_path); + free(s_card); + s_card = NULL; + return err; +} + +esp_err_t esp_vfs_fat_sdmmc_unmount() +{ + if (s_card == NULL) { + return ESP_ERR_INVALID_STATE; + } + // unmount + f_mount(0, "", 0); + // release SD driver + free(s_card); + s_card = NULL; + sdmmc_host_deinit(); + return esp_vfs_fat_unregister(); +} diff --git a/components/fatfs/test/component.mk b/components/fatfs/test/component.mk new file mode 100644 index 0000000000..ce464a212a --- /dev/null +++ b/components/fatfs/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/fatfs/test/test_fatfs.c b/components/fatfs/test/test_fatfs.c new file mode 100644 index 0000000000..9ee6606fb8 --- /dev/null +++ b/components/fatfs/test/test_fatfs.c @@ -0,0 +1,552 @@ +// Copyright 2015-2016 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. + +#include +#include +#include +#include +#include +#include +#include "unity.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_vfs.h" +#include "esp_vfs_fat.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +#include "diskio.h" +#include "ff.h" + +static const char* hello_str = "Hello, World!\n"; + +#define HEAP_SIZE_CAPTURE() \ + size_t heap_size = esp_get_free_heap_size(); + +#define HEAP_SIZE_CHECK(tolerance) \ + do {\ + size_t final_heap_size = esp_get_free_heap_size(); \ + if (final_heap_size < heap_size - tolerance) { \ + printf("Initial heap size: %d, final: %d, diff=%d\n", heap_size, final_heap_size, heap_size - final_heap_size); \ + } \ + } while(0) + +static void create_file_with_text(const char* name, const char* text) +{ + FILE* f = fopen(name, "wb"); + TEST_ASSERT_NOT_NULL(f); + TEST_ASSERT_TRUE(fputs(text, f) != EOF); + TEST_ASSERT_EQUAL(0, fclose(f)); +} + +TEST_CASE("can create and write file on sd card", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + create_file_with_text("/sdcard/hello.txt", hello_str); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("can read file on sd card", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + FILE* f = fopen("/sdcard/hello.txt", "r"); + TEST_ASSERT_NOT_NULL(f); + char buf[32]; + int cb = fread(buf, 1, sizeof(buf), f); + TEST_ASSERT_EQUAL(strlen(hello_str), cb); + TEST_ASSERT_EQUAL(0, strcmp(hello_str, buf)); + TEST_ASSERT_EQUAL(0, fclose(f)); + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +static void speed_test(void* buf, size_t buf_size, size_t file_size, bool write) +{ + const size_t buf_count = file_size / buf_size; + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = write, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + FILE* f = fopen("/sdcard/4mb.bin", (write) ? "wb" : "rb"); + TEST_ASSERT_NOT_NULL(f); + + struct timeval tv_start; + gettimeofday(&tv_start, NULL); + for (size_t n = 0; n < buf_count; ++n) { + if (write) { + TEST_ASSERT_EQUAL(1, fwrite(buf, buf_size, 1, f)); + } else { + if (fread(buf, buf_size, 1, f) != 1) { + printf("reading at n=%d, eof=%d", n, feof(f)); + TEST_FAIL(); + } + } + } + + struct timeval tv_end; + gettimeofday(&tv_end, NULL); + + TEST_ASSERT_EQUAL(0, fclose(f)); + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + + float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); + printf("%s %d bytes (block size %d) in %.3fms (%.3f MB/s)\n", + (write)?"Wrote":"Read", file_size, buf_size, t_s * 1e3, + (file_size / 1024 / 1024) / t_s); +} + + +TEST_CASE("read speed test", "[fatfs]") +{ + + HEAP_SIZE_CAPTURE(); + const size_t buf_size = 16 * 1024; + uint32_t* buf = (uint32_t*) calloc(1, buf_size); + const size_t file_size = 4 * 1024 * 1024; + speed_test(buf, 4 * 1024, file_size, false); + HEAP_SIZE_CHECK(0); + speed_test(buf, 8 * 1024, file_size, false); + HEAP_SIZE_CHECK(0); + speed_test(buf, 16 * 1024, file_size, false); + HEAP_SIZE_CHECK(0); + free(buf); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("write speed test", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + + const size_t buf_size = 16 * 1024; + uint32_t* buf = (uint32_t*) calloc(1, buf_size); + for (size_t i = 0; i < buf_size / 4; ++i) { + buf[i] = esp_random(); + } + const size_t file_size = 4 * 1024 * 1024; + + speed_test(buf, 4 * 1024, file_size, true); + speed_test(buf, 8 * 1024, file_size, true); + speed_test(buf, 16 * 1024, file_size, true); + + free(buf); + + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("can lseek", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + FILE* f = fopen("/sdcard/seek.txt", "wb+"); + TEST_ASSERT_NOT_NULL(f); + TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n")); + TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR)); + TEST_ASSERT_EQUAL('9', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET)); + TEST_ASSERT_EQUAL('3', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END)); + TEST_ASSERT_EQUAL('8', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_END)); + TEST_ASSERT_EQUAL(14, ftell(f)); + TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n")); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END)); + TEST_ASSERT_EQUAL(18, ftell(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET)); + char buf[20]; + TEST_ASSERT_EQUAL(18, fread(buf, 1, sizeof(buf), f)); + const char ref_buf[] = "0123456789\n\0\0\0abc\n"; + TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1); + + TEST_ASSERT_EQUAL(0, fclose(f)); + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("stat returns correct values", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + struct tm tm; + tm.tm_year = 2016 - 1900; + tm.tm_mon = 0; + tm.tm_mday = 10; + tm.tm_hour = 16; + tm.tm_min = 30; + tm.tm_sec = 0; + time_t t = mktime(&tm); + printf("Setting time: %s", asctime(&tm)); + struct timeval now = { .tv_sec = t }; + settimeofday(&now, NULL); + + create_file_with_text("/sdcard/stat.txt", "foo\n"); + + struct stat st; + TEST_ASSERT_EQUAL(0, stat("/sdcard/stat.txt", &st)); + time_t mtime = st.st_mtime; + struct tm mtm; + localtime_r(&mtime, &mtm); + printf("File time: %s", asctime(&mtm)); + TEST_ASSERT(abs(mtime - t) < 2); // fatfs library stores time with 2 second precision + + TEST_ASSERT(st.st_mode & S_IFREG); + TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("unlink removes a file", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + create_file_with_text("/sdcard/unlink.txt", "unlink\n"); + + TEST_ASSERT_EQUAL(0, unlink("/sdcard/unlink.txt")); + + TEST_ASSERT_NULL(fopen("/sdcard/unlink.txt", "r")); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("link copies a file, rename moves a file", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + unlink("/sdcard/linkcopy.txt"); + unlink("/sdcard/link_dst.txt"); + unlink("/sdcard/link_src.txt"); + + FILE* f = fopen("/sdcard/link_src.txt", "w+"); + TEST_ASSERT_NOT_NULL(f); + char* str = "0123456789"; + for (int i = 0; i < 4000; ++i) { + TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f)); + } + TEST_ASSERT_EQUAL(0, fclose(f)); + + TEST_ASSERT_EQUAL(0, link("/sdcard/link_src.txt", "/sdcard/linkcopy.txt")); + + FILE* fcopy = fopen("/sdcard/linkcopy.txt", "r"); + TEST_ASSERT_NOT_NULL(fcopy); + TEST_ASSERT_EQUAL(0, fseek(fcopy, 0, SEEK_END)); + TEST_ASSERT_EQUAL(40000, ftell(fcopy)); + TEST_ASSERT_EQUAL(0, fclose(fcopy)); + + TEST_ASSERT_EQUAL(0, rename("/sdcard/linkcopy.txt", "/sdcard/link_dst.txt")); + TEST_ASSERT_NULL(fopen("/sdcard/linkcopy.txt", "r")); + FILE* fdst = fopen("/sdcard/link_dst.txt", "r"); + TEST_ASSERT_NOT_NULL(fdst); + TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END)); + TEST_ASSERT_EQUAL(40000, ftell(fdst)); + TEST_ASSERT_EQUAL(0, fclose(fdst)); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +typedef struct { + const char* filename; + bool write; + size_t word_count; + int seed; + SemaphoreHandle_t done; + int result; +} read_write_test_arg_t; + +#define READ_WRITE_TEST_ARG_INIT(name, seed_) \ + { \ + .filename = name, \ + .seed = seed_, \ + .word_count = 8192, \ + .write = true, \ + .done = xSemaphoreCreateBinary() \ + } + +static void read_write_task(void* param) +{ + read_write_test_arg_t* args = (read_write_test_arg_t*) param; + FILE* f = fopen(args->filename, args->write ? "wb" : "rb"); + if (f == NULL) { + args->result = ESP_ERR_NOT_FOUND; + goto done; + } + + srand(args->seed); + for (size_t i = 0; i < args->word_count; ++i) { + uint32_t val = rand(); + if (args->write) { + int cnt = fwrite(&val, sizeof(val), 1, f); + if (cnt != 1) { + args->result = ESP_FAIL; + goto close; + } + } else { + uint32_t rval; + int cnt = fread(&rval, sizeof(rval), 1, f); + if (cnt != 1 || rval != val) { + ets_printf("E: i=%d, cnt=%d rval=%d val=%d\n\n", i, cnt, rval, val); + args->result = ESP_FAIL; + goto close; + } + } + } + args->result = ESP_OK; + +close: + fclose(f); + +done: + xSemaphoreGive(args->done); + vTaskDelay(1); + vTaskDelete(NULL); +} + + +TEST_CASE("multiple tasks can use same volume", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT("/sdcard/f1", 1); + read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT("/sdcard/f2", 2); + + printf("writing f1 and f2\n"); + + xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, 0); + xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, 1); + + xSemaphoreTake(args1.done, portMAX_DELAY); + printf("f1 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args1.result); + xSemaphoreTake(args2.done, portMAX_DELAY); + printf("f2 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args2.result); + + args1.write = false; + args2.write = false; + read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT("/sdcard/f3", 3); + read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT("/sdcard/f4", 4); + + printf("reading f1 and f2, writing f3 and f4\n"); + + xTaskCreatePinnedToCore(&read_write_task, "rw3", 2048, &args3, 3, NULL, 1); + xTaskCreatePinnedToCore(&read_write_task, "rw4", 2048, &args4, 3, NULL, 0); + xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, 0); + xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, 1); + + xSemaphoreTake(args1.done, portMAX_DELAY); + printf("f1 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args1.result); + xSemaphoreTake(args2.done, portMAX_DELAY); + printf("f2 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args2.result); + xSemaphoreTake(args3.done, portMAX_DELAY); + printf("f3 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args3.result); + xSemaphoreTake(args4.done, portMAX_DELAY); + printf("f4 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args4.result); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + vSemaphoreDelete(args1.done); + vSemaphoreDelete(args2.done); + vSemaphoreDelete(args3.done); + vSemaphoreDelete(args4.done); + vTaskDelay(10); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("can create and remove directories", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + TEST_ASSERT_EQUAL(0, mkdir("/sdcard/dir1", 0755)); + struct stat st; + TEST_ASSERT_EQUAL(0, stat("/sdcard/dir1", &st)); + TEST_ASSERT_TRUE(st.st_mode & S_IFDIR); + TEST_ASSERT_FALSE(st.st_mode & S_IFREG); + TEST_ASSERT_EQUAL(0, rmdir("/sdcard/dir1")); + TEST_ASSERT_EQUAL(-1, stat("/sdcard/dir1", &st)); + + TEST_ASSERT_EQUAL(0, mkdir("/sdcard/dir2", 0755)); + create_file_with_text("/sdcard/dir2/1.txt", "foo\n"); + TEST_ASSERT_EQUAL(0, stat("/sdcard/dir2", &st)); + TEST_ASSERT_TRUE(st.st_mode & S_IFDIR); + TEST_ASSERT_FALSE(st.st_mode & S_IFREG); + TEST_ASSERT_EQUAL(0, stat("/sdcard/dir2/1.txt", &st)); + TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); + TEST_ASSERT_TRUE(st.st_mode & S_IFREG); + TEST_ASSERT_EQUAL(-1, rmdir("/sdcard/dir2")); + TEST_ASSERT_EQUAL(0, unlink("/sdcard/dir2/1.txt")); + TEST_ASSERT_EQUAL(0, rmdir("/sdcard/dir2")); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + unlink("/sdcard/dir/inner/3.txt"); + rmdir("/sdcard/dir/inner"); + unlink("/sdcard/dir/2.txt"); + unlink("/sdcard/dir/1.txt"); + unlink("/sdcard/dir/boo.bin"); + rmdir("/sdcard/dir"); + + TEST_ASSERT_EQUAL(0, mkdir("/sdcard/dir", 0755)); + create_file_with_text("/sdcard/dir/2.txt", "1\n"); + create_file_with_text("/sdcard/dir/1.txt", "1\n"); + create_file_with_text("/sdcard/dir/boo.bin", "\01\02\03"); + TEST_ASSERT_EQUAL(0, mkdir("/sdcard/dir/inner", 0755)); + create_file_with_text("/sdcard/dir/inner/3.txt", "3\n"); + + DIR* dir = opendir("/sdcard/dir"); + TEST_ASSERT_NOT_NULL(dir); + int count = 0; + const char* names[4]; + while(count < 4) { + struct dirent* de = readdir(dir); + if (!de) { + break; + } + printf("found '%s'\n", de->d_name); + if (strcasecmp(de->d_name, "1.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "1.txt"; + ++count; + } else if (strcasecmp(de->d_name, "2.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "2.txt"; + ++count; + } else if (strcasecmp(de->d_name, "inner") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_DIR); + names[count] = "inner"; + ++count; + } else if (strcasecmp(de->d_name, "boo.bin") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "boo.bin"; + ++count; + } else { + TEST_FAIL_MESSAGE("unexpected directory entry"); + } + } + TEST_ASSERT_EQUAL(count, 4); + + rewinddir(dir); + struct dirent* de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0])); + seekdir(dir, 3); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3])); + seekdir(dir, 1); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1])); + seekdir(dir, 2); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2])); + + TEST_ASSERT_EQUAL(0, closedir(dir)); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index f03da6bc01..15178539b4 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -3,7 +3,7 @@ menu "FreeRTOS" # This is actually also handled in the ESP32 startup code, not only in FreeRTOS. config FREERTOS_UNICORE bool "Run FreeRTOS only on first core" - default y + default n help This version of FreeRTOS normally takes control of all cores of the CPU. Select this if you only want to start it on the first core. @@ -54,7 +54,7 @@ config FREERTOS_ASSERT_ON_UNTESTED_FUNCTION choice FREERTOS_CHECK_STACKOVERFLOW prompt "Check for stack overflow" - default FREERTOS_CHECK_STACKOVERFLOW_QUICK + default FREERTOS_CHECK_STACKOVERFLOW_CANARY help FreeRTOS can check for stack overflows in threads and trigger an user function called vApplicationStackOverflowHook when this happens. @@ -187,6 +187,7 @@ config FREERTOS_PORTMUX_DEBUG If enabled, debug information (including integrity checks) will be printed to UART for the port-specific MUX implementation. +if !FREERTOS_UNICORE config FREERTOS_PORTMUX_DEBUG_RECURSIVE bool "Debug portMUX Recursion" depends on FREERTOS_PORTMUX_DEBUG @@ -194,8 +195,23 @@ config FREERTOS_PORTMUX_DEBUG_RECURSIVE help If enabled, additional debug information will be printed for recursive portMUX usage. +endif #FREERTOS_UNICORE +config FREERTOS_WATCHPOINT_END_OF_STACK + bool "Set a debug watchpoint at the end of each stack" + default n + help + FreeRTOS can check if a stack has overflown its bounds by checking either the value of + the stack pointer or by checking the integrity of canary bytes. (See FREERTOS_CHECK_STACKOVERFLOW + for more information.) These checks only happen on a context switch, and the situation that caused + the stack overflow may already be long gone by then. This option will use the debug memory + watchpoint 1 (the second one) to allow breaking into the debugger (or panic'ing) as soon as any + of the last 32 bytes on the stack of a task are overwritten. The side effect is that using gdb, you + effectively only have one watchpoint; the 2nd one is overwritten as soon as a task switch happens. + + When this watchpoint is hit, gdb will stop with a SIGTRAP message. When no OCD is attached, esp-idf + will panic on an unhandled debug exception. endif # FREERTOS_DEBUG_INTERNALS diff --git a/components/freertos/include/freertos/FreeRTOS.h b/components/freertos/include/freertos/FreeRTOS.h index 4c60308f78..0e93acf271 100644 --- a/components/freertos/include/freertos/FreeRTOS.h +++ b/components/freertos/include/freertos/FreeRTOS.h @@ -863,8 +863,8 @@ typedef struct xSTATIC_TCB void *pxDummy6; uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; UBaseType_t uxDummyCoreId; - #if ( portSTACK_GROWTH > 0 ) - void *pxDummy8; + #if ( portSTACK_GROWTH > 0 || configENABLE_TASK_SNAPSHOT == 1 ) + void *pxDummy8; #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) UBaseType_t uxDummy9; diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 4f1012657a..b2fc077bc3 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -268,7 +268,9 @@ #define configXT_BOARD 1 /* Board mode */ #define configXT_SIMULATOR 0 - +#if CONFIG_ESP32_ENABLE_COREDUMP +#define configENABLE_TASK_SNAPSHOT 1 +#endif #endif /* FREERTOS_CONFIG_H */ diff --git a/components/freertos/include/freertos/portable.h b/components/freertos/include/freertos/portable.h index 9ed378a8ab..b05755da40 100644 --- a/components/freertos/include/freertos/portable.h +++ b/components/freertos/include/freertos/portable.h @@ -187,6 +187,13 @@ void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; void vPortYieldOtherCore( BaseType_t coreid) PRIVILEGED_FUNCTION; + +/* + Callback to set a watchpoint on the end of the stack. Called every context switch to change the stack + watchpoint around. + */ +void vPortSetStackWatchpoint( void* pxStackStart ); + /* * The structures and methods of manipulating the MPU are contained within the * port layer. diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index f20a4a1e26..7cae4b05b6 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -213,7 +213,6 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux); #define portEXIT_CRITICAL_ISR(mux) vPortCPUReleaseMutex(mux) #endif - // Cleaner and preferred solution allows nested interrupts disabling and restoring via local registers or stack. // They can be called from interrupts too. //NOT SMP-COMPATIBLE! Use only if all you want is to disable the interrupts locally! diff --git a/components/freertos/include/freertos/task.h b/components/freertos/include/freertos/task.h index f7b9181fcf..590b07a2a4 100644 --- a/components/freertos/include/freertos/task.h +++ b/components/freertos/include/freertos/task.h @@ -181,6 +181,18 @@ typedef struct xTASK_STATUS uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ } TaskStatus_t; +/* + * Used with the uxTaskGetSnapshotAll() function to save memory snapshot of each task in the system. + * We need this struct because TCB_t is defined (hidden) in tasks.c. + */ +typedef struct xTASK_SNAPSHOT +{ + void *pxTCB; /* Address of task control block. */ + StackType_t *pxTopOfStack; /* Points to the location of the last item placed on the tasks stack. */ + StackType_t *pxEndOfStack; /* Points to the end of the stack. pxTopOfStack < pxEndOfStack, stack grows hi2lo + pxTopOfStack > pxEndOfStack, stack grows lo2hi*/ +} TaskSnapshot_t; + /* Possible return values for eTaskConfirmSleepModeStatus(). */ typedef enum { @@ -2173,6 +2185,17 @@ eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; */ void *pvTaskIncrementMutexHeldCount( void ); +/* + * This function fills array with TaskSnapshot_t structures for every task in the system. + * Used by core dump facility to get snapshots of all tasks in the system. + * Only available when configENABLE_TASK_SNAPSHOT is set to 1. + * @param pxTaskSnapshotArray Pointer to array of TaskSnapshot_t structures to store tasks snapshot data. + * @param uxArraySize Size of tasks snapshots array. + * @param pxTcbSz Pointer to store size of TCB. + * @return Number of elements stored in array. + */ +UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ); + #ifdef __cplusplus } #endif diff --git a/components/freertos/port.c b/components/freertos/port.c index 25480ed475..c778950d6d 100644 --- a/components/freertos/port.c +++ b/components/freertos/port.c @@ -282,6 +282,7 @@ void vPortCPUAcquireMutex(portMUX_TYPE *mux, const char *fnName, int line) { #else void vPortCPUAcquireMutex(portMUX_TYPE *mux) { #endif +#if !CONFIG_FREERTOS_UNICORE uint32_t res; uint32_t recCnt; unsigned int irqStatus; @@ -324,6 +325,7 @@ void vPortCPUAcquireMutex(portMUX_TYPE *mux) { } #endif portEXIT_CRITICAL_NESTED(irqStatus); +#endif } /* @@ -335,6 +337,7 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux, const char *fnName, int li #else portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) { #endif +#if !CONFIG_FREERTOS_UNICORE uint32_t res=0; uint32_t recCnt; unsigned int irqStatus; @@ -379,6 +382,9 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) { } portEXIT_CRITICAL_NESTED(irqStatus); return ret; +#else //!CONFIG_FREERTOS_UNICORE + return 0; +#endif } #if CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG @@ -388,3 +394,19 @@ void vPortFirstTaskHook(TaskFunction_t function) { #endif +void vPortSetStackWatchpoint( void* pxStackStart ) { + //Set watchpoint 1 to watch the last 32 bytes of the stack. + //Unfortunately, the Xtensa watchpoints can't set a watchpoint on a random [base - base+n] region because + //the size works by masking off the lowest address bits. For that reason, we futz a bit and watch the lowest 32 + //bytes of the stack we can actually watch. In general, this can cause the watchpoint to be triggered at most + //28 bytes early. The value 32 is chosen because it's larger than the stack canary, which in FreeRTOS is 20 bytes. + //This way, we make sure we trigger before/when the stack canary is corrupted, not after. + int addr=(int)pxStackStart; + addr=(addr+31)&(~31); + esp_set_watchpoint(1, (char*)addr, 32, ESP_WATCHPOINT_STORE); +} + + + + + diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 16cce3b967..6caabaa38e 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -78,6 +78,7 @@ task.h is included from an application file. */ #include "rom/ets_sys.h" #include "esp_newlib.h" +#include "esp_panic.h" /* FreeRTOS includes. */ #include "FreeRTOS.h" @@ -180,7 +181,7 @@ typedef struct tskTaskControlBlock char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ BaseType_t xCoreID; /*< Core this task is pinned to */ /* If this moves around (other than pcTaskName size changes), please change the define in xtensa_vectors.S as well. */ - #if ( portSTACK_GROWTH > 0 ) + #if ( portSTACK_GROWTH > 0 || configENABLE_TASK_SNAPSHOT == 1 ) StackType_t *pxEndOfStack; /*< Points to the end of the stack on architectures where the stack grows up from low memory. */ #endif @@ -369,7 +370,7 @@ PRIVILEGED_DATA static portMUX_TYPE xTickCountMutex = portMUX_INITIALIZER_UNLOCK \ /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \ the same priority get an equal share of the processor time. */ \ - listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB[ xPortGetCoreID() ], &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); \ + listGET_OWNER_OF_NEXT_ENTRY( xTaskGetCurrentTaskHandle(), &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); \ } /* taskSELECT_HIGHEST_PRIORITY_TASK */ /*-----------------------------------------------------------*/ @@ -398,7 +399,7 @@ PRIVILEGED_DATA static portMUX_TYPE xTickCountMutex = portMUX_INITIALIZER_UNLOCK /* Find the highest priority queue that contains ready tasks. */ \ portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ - listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB[ xPortGetCoreID() ], &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + listGET_OWNER_OF_NEXT_ENTRY( xTaskGetCurrentTaskHandle(), &( pxReadyTasksLists[ uxTopPriority ] ) ); \ } /* taskSELECT_HIGHEST_PRIORITY_TASK() */ /*-----------------------------------------------------------*/ @@ -456,7 +457,7 @@ count overflows. */ * see if the parameter is NULL and returns a pointer to the appropriate TCB. */ /* ToDo: See if this still works for multicore. */ -#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB[ xPortGetCoreID() ] : ( TCB_t * ) ( pxHandle ) ) +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) xTaskGetCurrentTaskHandle() : ( TCB_t * ) ( pxHandle ) ) /* The item value of the event list item is normally used to hold the priority of the task to which it belongs (coded to allow it to be held in reverse @@ -631,9 +632,11 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode */ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority ) { + TCB_t *curTCB = xTaskGetCurrentTaskHandle(); BaseType_t i; + if (xCoreID != tskNO_AFFINITY) { - if ( pxCurrentTCB[ xCoreID ]->uxPriority < uxPriority ) { + if ( curTCB->uxPriority < uxPriority ) { vPortYieldOtherCore( xCoreID ); } } @@ -882,6 +885,12 @@ UBaseType_t x; /* Check the alignment of the calculated top of stack is correct. */ configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + #if ( configENABLE_TASK_SNAPSHOT == 1 ) + { + /* need stack end for core dumps */ + pxNewTCB->pxEndOfStack = pxTopOfStack; + } +#endif } #else /* portSTACK_GROWTH */ { @@ -1039,6 +1048,7 @@ UBaseType_t x; static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) { + TCB_t *curTCB; BaseType_t i; /* Ensure interrupts don't access the task lists while the lists are being @@ -1111,23 +1121,25 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode portSETUP_TCB( pxNewTCB ); } + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; taskEXIT_CRITICAL(&xTaskQueueMutex); if( xSchedulerRunning != pdFALSE ) { + taskENTER_CRITICAL(&xTaskQueueMutex); /* Scheduler is running. If the created task is of a higher priority than an executing task then it should run now. ToDo: This only works for the current core. If a task is scheduled on an other processor, the other processor will keep running the task it's working on, and only switch to the newer task on a timer interrupt. */ //No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. - if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < pxNewTCB->uxPriority ) + if( curTCB->uxPriority < pxNewTCB->uxPriority ) { /* Scheduler is running. If the created task is of a higher priority than an executing task then it should run now. No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. */ - if( tskCAN_RUN_HERE( xCoreID ) && pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < pxNewTCB->uxPriority ) + if( tskCAN_RUN_HERE( xCoreID ) && curTCB->uxPriority < pxNewTCB->uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } @@ -1143,6 +1155,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode { mtCOVERAGE_TEST_MARKER(); } + taskEXIT_CRITICAL(&xTaskQueueMutex); } else { @@ -1409,11 +1422,12 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode eTaskState eReturn; List_t *pxStateList; const TCB_t * const pxTCB = ( TCB_t * ) xTask; + TCB_t * curTCB = xTaskGetCurrentTaskHandle(); UNTESTED_FUNCTION(); configASSERT( pxTCB ); - if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] ) + if( pxTCB == curTCB ) { /* The task calling this function is querying its own state. */ eReturn = eRunning; @@ -1691,6 +1705,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode void vTaskSuspend( TaskHandle_t xTaskToSuspend ) { TCB_t *pxTCB; + TCB_t *curTCB; UNTESTED_FUNCTION(); taskENTER_CRITICAL(&xTaskQueueMutex); @@ -1723,10 +1738,11 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode } vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ); + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; } taskEXIT_CRITICAL(&xTaskQueueMutex); - if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] ) + if( pxTCB == curTCB ) { if( xSchedulerRunning != pdFALSE ) { @@ -2034,7 +2050,7 @@ void vTaskEndScheduler( void ) //Return global reent struct if FreeRTOS isn't running, struct _reent* __getreent() { //No lock needed because if this changes, we won't be running anymore. - TCB_t *currTask=pxCurrentTCB[ xPortGetCoreID() ]; + TCB_t *currTask=xTaskGetCurrentTaskHandle(); if (currTask==NULL) { //No task running. Return global struct. return _GLOBAL_REENT; @@ -2052,7 +2068,11 @@ void vTaskSuspendAll( void ) BaseType_t. Please read Richard Barry's reply in the following link to a post in the FreeRTOS support forum before reporting this as a bug! - http://goo.gl/wu4acr */ + unsigned state; + + state = portENTER_CRITICAL_NESTED(); ++uxSchedulerSuspended[ xPortGetCoreID() ]; + portEXIT_CRITICAL_NESTED(state); } /*----------------------------------------------------------*/ @@ -2595,7 +2615,7 @@ BaseType_t xSwitchRequired = pdFALSE; /* If xTask is NULL then we are setting our own task hook. */ if( xTask == NULL ) { - xTCB = ( TCB_t * ) pxCurrentTCB[ xPortGetCoreID() ]; + xTCB = ( TCB_t * ) xTaskGetCurrentTaskHandle(); } else { @@ -2626,7 +2646,7 @@ BaseType_t xSwitchRequired = pdFALSE; /* If xTask is NULL then we are calling our own task hook. */ if( xTask == NULL ) { - xTCB = ( TCB_t * ) pxCurrentTCB[ xPortGetCoreID() ]; + xTCB = ( TCB_t * ) xTaskGetCurrentTaskHandle(); } else { @@ -2796,6 +2816,11 @@ void vTaskSwitchContext( void ) traceTASK_SWITCHED_IN(); +#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK + vPortSetStackWatchpoint(pxCurrentTCB[xPortGetCoreID()]->pxStack); +#endif + + } portEXIT_CRITICAL_NESTED(irqstate); } @@ -3387,8 +3412,8 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) { - pxTCB = prvGetTCBFromHandle( xTaskToSet ); taskENTER_CRITICAL(&xTaskQueueMutex); + pxTCB = prvGetTCBFromHandle( xTaskToSet ); pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; pxTCB->pvThreadLocalStoragePointersDelCallback[ xIndex ] = xDelCallback; taskEXIT_CRITICAL(&xTaskQueueMutex); @@ -3408,8 +3433,10 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) { + taskENTER_CRITICAL(&xTaskQueueMutex); pxTCB = prvGetTCBFromHandle( xTaskToSet ); pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; + taskEXIT_CRITICAL(&xTaskQueueMutex); } } #endif /* configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS */ @@ -3496,26 +3523,23 @@ static void prvCheckTasksWaitingTermination( void ) /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called too often in the idle task. */ + taskENTER_CRITICAL(&xTaskQueueMutex); while( uxTasksDeleted > ( UBaseType_t ) 0U ) { - taskENTER_CRITICAL(&xTaskQueueMutex); { xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); } - taskEXIT_CRITICAL(&xTaskQueueMutex); if( xListIsEmpty == pdFALSE ) { TCB_t *pxTCB; - taskENTER_CRITICAL(&xTaskQueueMutex); { pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); --uxCurrentNumberOfTasks; --uxTasksDeleted; } - taskEXIT_CRITICAL(&xTaskQueueMutex); #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) { @@ -3535,7 +3559,8 @@ static void prvCheckTasksWaitingTermination( void ) { mtCOVERAGE_TEST_MARKER(); } - } + } + taskEXIT_CRITICAL(&xTaskQueueMutex); } #endif /* vTaskDelete */ } @@ -3805,11 +3830,11 @@ TCB_t *pxTCB; TaskHandle_t xTaskGetCurrentTaskHandle( void ) { TaskHandle_t xReturn; + unsigned state; - /* A critical section is not required as this is not called from - an interrupt and the current TCB will always be the same for any - individual execution thread. */ + state = portENTER_CRITICAL_NESTED(); xReturn = pxCurrentTCB[ xPortGetCoreID() ]; + portEXIT_CRITICAL_NESTED(state); return xReturn; } @@ -3835,7 +3860,9 @@ TCB_t *pxTCB; BaseType_t xTaskGetSchedulerState( void ) { BaseType_t xReturn; + unsigned state; + state = portENTER_CRITICAL_NESTED(); if( xSchedulerRunning == pdFALSE ) { xReturn = taskSCHEDULER_NOT_STARTED; @@ -3851,6 +3878,7 @@ TCB_t *pxTCB; xReturn = taskSCHEDULER_SUSPENDED; } } + portEXIT_CRITICAL_NESTED(state); return xReturn; } @@ -4386,6 +4414,8 @@ TickType_t uxReturn; void *pvTaskIncrementMutexHeldCount( void ) { + TCB_t *curTCB; + /* If xSemaphoreCreateMutex() is called before any tasks have been created then pxCurrentTCB will be NULL. */ taskENTER_CRITICAL(&xTaskQueueMutex); @@ -4393,9 +4423,10 @@ TickType_t uxReturn; { ( pxCurrentTCB[ xPortGetCoreID() ]->uxMutexesHeld )++; } + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; taskEXIT_CRITICAL(&xTaskQueueMutex); - return pxCurrentTCB[ xPortGetCoreID() ]; + return curTCB; } #endif /* configUSE_MUTEXES */ @@ -4887,6 +4918,82 @@ TickType_t uxReturn; #endif /* configUSE_TASK_NOTIFICATIONS */ +#if ( configENABLE_TASK_SNAPSHOT == 1 ) + + static void prvTaskGetSnapshotsFromList( TaskSnapshot_t *pxTaskSnapshotArray, UBaseType_t *uxTask, const UBaseType_t uxArraySize, List_t *pxList ) + { + TCB_t *pxNextTCB, *pxFirstTCB; + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + + if( *uxTask >= uxArraySize ) + break; + + pxTaskSnapshotArray[ *uxTask ].pxTCB = pxNextTCB; + pxTaskSnapshotArray[ *uxTask ].pxTopOfStack = (StackType_t *)pxNextTCB->pxTopOfStack; + #if( portSTACK_GROWTH < 0 ) + { + pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxNextTCB->pxEndOfStack; + } + #else + { + pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxNextTCB->pxStack; + } + #endif + + (*uxTask)++; + + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ) + { + UBaseType_t uxTask = 0, i = 0; + + *pxTcbSz = sizeof(TCB_t); + + { + /* Fill in an TaskStatus_t structure with information on each + task in the Ready state. */ + i = configMAX_PRIORITIES; + do + { + i--; + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( pxReadyTasksLists[ i ] ) ); + } while( i > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Fill in an TaskStatus_t structure with information on each + task in the Blocked state. */ + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxDelayedTaskList ); + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxOverflowDelayedTaskList ); + + #if( INCLUDE_vTaskDelete == 1 ) + { + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &xTasksWaitingTermination ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &xSuspendedTaskList ); + } + #endif + } + return uxTask; + } + +#endif + #ifdef FREERTOS_MODULE_TEST #include "tasks_test_access_functions.h" #endif diff --git a/components/freertos/test/test_freertos_eventgroups.c b/components/freertos/test/test_freertos_eventgroups.c index 35a5cc4ed2..b17e127c28 100644 --- a/components/freertos/test/test_freertos_eventgroups.c +++ b/components/freertos/test/test_freertos_eventgroups.c @@ -34,7 +34,7 @@ static void task_event_group_call_response(void *param) printf("Task %d done\n", task_num); /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ - vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelete(NULL); } @@ -85,7 +85,7 @@ static void task_test_sync(void *param) printf("Done %d = %x\n", task_num, after_done); /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ - vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelete(NULL); } diff --git a/components/idf_test/integration_test/InitialConditionAll.yml b/components/idf_test/integration_test/InitialConditionAll.yml index 3821894552..5d4ce7ab3f 100644 --- a/components/idf_test/integration_test/InitialConditionAll.yml +++ b/components/idf_test/integration_test/InitialConditionAll.yml @@ -260,6 +260,8 @@ initial condition: - ['R SSC1 C +MAC:AP,OK'] - - SSC SSC1 dhcp -S -o 2 - [R SSC1 C +DHCP] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC1 ap -S -s -p -t - ['R SSC1 C +SAP:OK'] initial condition detail: testing ap on sta + ap mode (autogen by APM1) @@ -271,6 +273,8 @@ initial condition: - ['R SSC1 C +MAC:AP,OK'] - - SSC SSC1 dhcp -S -o 2 - [R SSC1 C +DHCP] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC1 ap -S -s -p -t - ['R SSC1 C +SAP:OK'] restore post cmd set: @@ -305,6 +309,8 @@ initial condition: - ['R SSC1 C +MAC:AP,OK'] - - SSC SSC1 dhcp -S -o 2 - [R SSC1 C +DHCP] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC1 ap -S -s -p -t - ['R SSC1 C +SAP:OK'] - - WIFI CONN @@ -319,6 +325,8 @@ initial condition: - ['R SSC1 C +MAC:AP,OK'] - - SSC SSC1 dhcp -S -o 2 - [R SSC1 C +DHCP] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC1 ap -S -s -p -t - ['R SSC1 C +SAP:OK'] - - WIFI CONN @@ -2854,6 +2862,8 @@ initial condition: - ['R SSC1 C +MODE:OK'] - - SSC SSC2 op -S -o 3 - ['R SSC2 C +MODE:OK'] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC2 sta -D - ['R SSC2 C +QAP:'] - - SSC SSC2 soc -T @@ -2873,6 +2883,8 @@ initial condition: - ['R SSC1 C +MODE:OK'] - - SSC SSC2 op -S -o 3 - ['R SSC2 C +MODE:OK'] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC2 sta -D - ['R SSC2 C +QAP:'] - - SSC SSC2 soc -T diff --git a/components/idf_test/integration_test/KnownIssues b/components/idf_test/integration_test/KnownIssues index e0991f39b0..884bd2f590 100644 --- a/components/idf_test/integration_test/KnownIssues +++ b/components/idf_test/integration_test/KnownIssues @@ -5,24 +5,6 @@ TCPIP_ICMP_0101 ^TCPIP_ICMP_0101 -# IGMP cases are not supported for now -TCPIP_IGMP_0101 -TCPIP_IGMP_0102 -TCPIP_IGMP_0103 -TCPIP_IGMP_0104 -TCPIP_IGMP_0201 -TCPIP_IGMP_0202 -TCPIP_IGMP_0203 -TCPIP_IGMP_0204 -^TCPIP_IGMP_0101 -^TCPIP_IGMP_0102 -^TCPIP_IGMP_0103 -^TCPIP_IGMP_0104 -^TCPIP_IGMP_0201 -^TCPIP_IGMP_0202 -^TCPIP_IGMP_0203 -^TCPIP_IGMP_0204 - # don't support PHY mode command WIFI_SCAN_0201 WIFI_SCAN_0302 @@ -48,59 +30,181 @@ WIFI_CONN_0801 ^WIFI_CONN_0801 # disconnect reason -WIFI_CONN_0904 -^WIFI_CONN_0904 WIFI_CONN_0901 ^WIFI_CONN_0901 +WIFI_CONN_0904 +^WIFI_CONN_0904 +^WIFI_CONN_0902 +WIFI_CONN_0902 # Wifi connect issue +WIFI_CONN_0101 +^WIFI_CONN_0101 +WIFI_CONN_0102 +^WIFI_CONN_0102 +WIFI_CONN_0103 +^WIFI_CONN_0103 WIFI_CONN_0104 ^WIFI_CONN_0104 +WIFI_CONN_0201 +^WIFI_CONN_0201 +WIFI_CONN_0401 +^WIFI_CONN_0401 +WIFI_CONN_0601 ^WIFI_CONN_0601 +WIFI_ADDR_0102 +^WIFI_ADDR_0102 +WIFI_CONN_0502 +^WIFI_CONN_0502 +WIFI_CONN_0501 +^WIFI_CONN_0501 # Wifi scan issue -WIFI_SCAN_0303 +WIFI_SCAN_0101 +^WIFI_SCAN_0101 +WIFI_SCAN_0102 +^WIFI_SCAN_0102 +WIFI_SCAN_0103 ^WIFI_SCAN_0103 +WIFI_SCAN_0104 +^WIFI_SCAN_0104 +WIFI_SCAN_0105 ^WIFI_SCAN_0105 +WIFI_SCAN_0303 +^WIFI_SCAN_0303 +WIFI_SCAN_0304 +^WIFI_SCAN_0304 +WIFI_MODE_0101 +WIFI_MODE_0102 +WIFI_MODE_0103 -# set mac address may lead to exception -WIFI_ADDR_0101 -^WIFI_ADDR_0101 +# IGMP cases are supported but as UDP is not stable, exclude them first +TCPIP_IGMP_0101 +TCPIP_IGMP_0102 +TCPIP_IGMP_0103 +TCPIP_IGMP_0104 +TCPIP_IGMP_0201 +TCPIP_IGMP_0202 +TCPIP_IGMP_0203 +TCPIP_IGMP_0204 +^TCPIP_IGMP_0101 +^TCPIP_IGMP_0102 +^TCPIP_IGMP_0103 +^TCPIP_IGMP_0104 +^TCPIP_IGMP_0201 +^TCPIP_IGMP_0202 +^TCPIP_IGMP_0203 +^TCPIP_IGMP_0204 # DHCP issues -^TCPIP_DHCP_0301 -TCPIP_DHCP_0301 TCPIP_DHCP_0101 +^TCPIP_DHCP_0101 +TCPIP_DHCP_0202 +^TCPIP_DHCP_0202 +TCPIP_DHCP_0204 +^TCPIP_DHCP_0204 +TCPIP_DHCP_0205 +^TCPIP_DHCP_0205 +TCPIP_DHCP_0206 +^TCPIP_DHCP_0206 TCPIP_DHCP_0207 ^TCPIP_DHCP_0207 TCPIP_DHCP_0208 ^TCPIP_DHCP_0208 -TCPIP_DHCP_0205 -^TCPIP_DHCP_0205 TCPIP_DHCP_0209 ^TCPIP_DHCP_0209 +TCPIP_DHCP_0210 +^TCPIP_DHCP_0210 +TCPIP_DHCP_0211 +^TCPIP_DHCP_0211 +TCPIP_DHCP_0301 +^TCPIP_DHCP_0301 +TCPIP_DHCP_0302 +^TCPIP_DHCP_0302 # TCP issue -TCPIP_TCP_0402 -^TCPIP_TCP_0406 -^TCPIP_TCP_0401 -TCPIP_TCP_0210 -^TCPIP_TCP_0210 +TCPIP_TCP_0101 +^TCPIP_TCP_0101 +TCPIP_TCP_0102 +^TCPIP_TCP_0102 TCPIP_TCP_0103 ^TCPIP_TCP_0103 +TCPIP_TCP_0104 +^TCPIP_TCP_0104 +TCPIP_TCP_0105 +^TCPIP_TCP_0105 +TCPIP_TCP_0106 +^TCPIP_TCP_0106 +TCPIP_TCP_0107 +^TCPIP_TCP_0107 TCPIP_TCP_0112 ^TCPIP_TCP_0112 - +TCPIP_TCP_0201 +^TCPIP_TCP_0201 +TCPIP_TCP_0202 +^TCPIP_TCP_0202 +TCPIP_TCP_0203 +^TCPIP_TCP_0203 +TCPIP_TCP_0204 +^TCPIP_TCP_0204 +TCPIP_TCP_0206 +^TCPIP_TCP_0206 +TCPIP_TCP_0208 +^TCPIP_TCP_0208 +TCPIP_TCP_0210 +^TCPIP_TCP_0210 +TCPIP_TCP_0401 +^TCPIP_TCP_0401 +TCPIP_TCP_0402 +^TCPIP_TCP_0402 +TCPIP_TCP_0403 +^TCPIP_TCP_0403 +TCPIP_TCP_0404 +^TCPIP_TCP_0404 +TCPIP_TCP_0406 +^TCPIP_TCP_0406 +TCPIP_TCP_0407 +^TCPIP_TCP_0407 +TCPIP_TCP_0408 +^TCPIP_TCP_0408 +TCPIP_TCP_0412 +^TCPIP_TCP_0412 +TCPIP_TCP_0411 +^TCPIP_TCP_0411 # UDP issue +TCPIP_UDP_0102 +^TCPIP_UDP_0102 TCPIP_UDP_0103 ^TCPIP_UDP_0103 +TCPIP_UDP_0104 +^TCPIP_UDP_0104 +TCPIP_UDP_0108 +^TCPIP_UDP_0108 TCPIP_UDP_0110 ^TCPIP_UDP_0110 +TCPIP_UDP_0112 +^TCPIP_UDP_0112 +TCPIP_UDP_0301 +^TCPIP_UDP_0301 +TCPIP_UDP_0302 +^TCPIP_UDP_0302 +TCPIP_UDP_0303 +^TCPIP_UDP_0303 +TCPIP_UDP_0304 +^TCPIP_UDP_0304 TCPIP_UDP_0305 ^TCPIP_UDP_0305 -^TCPIP_UDP_0304 -TCPIP_UDP_0104 - +TCPIP_UDP_0306 +^TCPIP_UDP_0306 +TCPIP_UDP_0307 +^TCPIP_UDP_0307 +#DNS +TCPIP_DNS_0101 +^TCPIP_DNS_0101 +TCPIP_DNS_0102 +^TCPIP_DNS_0102 +TCPIP_DNS_0103 +^TCPIP_DNS_0103 diff --git a/components/json/include/cJSON.h b/components/json/include/cJSON.h index 466d10dbd0..92ed8c3b48 100644 --- a/components/json/include/cJSON.h +++ b/components/json/include/cJSON.h @@ -90,6 +90,7 @@ extern cJSON *cJSON_CreateTrue(void); extern cJSON *cJSON_CreateFalse(void); extern cJSON *cJSON_CreateBool(int b); extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateDouble(double num,int i_num); extern cJSON *cJSON_CreateString(const char *string); extern cJSON *cJSON_CreateArray(void); extern cJSON *cJSON_CreateObject(void); diff --git a/components/json/library/cJSON.c b/components/json/library/cJSON.c index ab3043711e..2a5c392161 100644 --- a/components/json/library/cJSON.c +++ b/components/json/library/cJSON.c @@ -694,6 +694,7 @@ cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->ty cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateDouble(double num,int i_num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=i_num;}return item;} cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} diff --git a/components/log/README.rst b/components/log/README.rst index d378179c8f..002dadf812 100644 --- a/components/log/README.rst +++ b/components/log/README.rst @@ -4,11 +4,11 @@ Logging library Overview -------- -Log library has two ways of managing log verbosity: compile time, set via menuconfig; and runtime, using ``esp_log_set_level`` function. +Log library has two ways of managing log verbosity: compile time, set via menuconfig; and runtime, using ``esp_log_level_set`` function. At compile time, filtering is done using ``CONFIG_LOG_DEFAULT_LEVEL`` macro, set via menuconfig. All logging statments for levels higher than ``CONFIG_LOG_DEFAULT_LEVEL`` will be removed by the preprocessor. -At run time, all logs below ``CONFIG_LOG_DEFAULT_LEVEL`` are enabled by default. ``esp_log_set_level`` function may be used to set logging level per module. Modules are identified by their tags, which are human-readable ASCII zero-terminated strings. +At run time, all logs below ``CONFIG_LOG_DEFAULT_LEVEL`` are enabled by default. ``esp_log_level_set`` function may be used to set logging level per module. Modules are identified by their tags, which are human-readable ASCII zero-terminated strings. How to use this library ----------------------- @@ -51,11 +51,11 @@ At component scope, define it in component makefile: CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG -To configure logging output per module at runtime, add calls to ``esp_log_set_level`` function: +To configure logging output per module at runtime, add calls to ``esp_log_level_set`` function: .. code-block:: c - esp_log_set_level("*", ESP_LOG_ERROR); // set all components to ERROR level - esp_log_set_level("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack - esp_log_set_level("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client + esp_log_level_set("*", ESP_LOG_ERROR); // set all components to ERROR level + esp_log_level_set("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack + esp_log_level_set("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index 33bc10b42a..6fda8d6054 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -75,6 +75,16 @@ void esp_log_set_vprintf(vprintf_like_t func); */ uint32_t esp_log_timestamp(void); +/** + * @brief Function which returns timestamp to be used in log output + * + * This function uses HW cycle counter and does not depend on OS, + * so it can be safely used after application crash. + * + * @return timestamp, in milliseconds + */ +uint32_t esp_log_early_timestamp(void); + /** * @brief Write message into the log * diff --git a/components/log/log.c b/components/log/log.c index 9670b82dfd..3826e173ba 100644 --- a/components/log/log.c +++ b/components/log/log.c @@ -15,7 +15,7 @@ /* * Log library — implementation notes. * - * Log library stores all tags provided to esp_log_set_level as a linked + * Log library stores all tags provided to esp_log_level_set as a linked * list. See uncached_tag_entry_t structure. * * To avoid looking up log level for given tag each time message is diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 2e7e31a8a9..760e035c8c 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -40,6 +40,12 @@ config LWIP_SO_REUSE Enabling this option allows binding to a port which remains in TIME_WAIT. +config LWIP_SO_RCVBUF + bool "Enable SO_RCVBUF option" + default 0 + help + Enabling this option allows checking for available data on a netconn. + config LWIP_DHCP_MAX_NTP_SERVERS int "Maximum number of NTP servers" default 1 @@ -49,6 +55,19 @@ config LWIP_DHCP_MAX_NTP_SERVERS First argument of sntp_setserver/sntp_setservername functions is limited to this value. +config LWIP_IP_FRAG + bool "Enable fragment outgoing IP packets" + default 0 + help + Enabling this option allows fragmenting outgoing IP packets if their size + exceeds MTU. + +config LWIP_IP_REASSEMBLY + bool "Enable reassembly incoming fragmented IP packets" + default 0 + help + Enabling this option allows reassemblying incoming fragmented IP packets. + endmenu diff --git a/components/lwip/api/api_lib.c b/components/lwip/api/api_lib.c index ecebf4f813..087115f0b9 100755 --- a/components/lwip/api/api_lib.c +++ b/components/lwip/api/api_lib.c @@ -508,6 +508,12 @@ netconn_recv_data(struct netconn *conn, void **new_buf) } #endif /* (LWIP_UDP || LWIP_RAW) */ +#if ESP_PERF + if (len > DBG_PERF_FILTER_LEN) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_SOC_IN); + } +#endif + #if LWIP_SO_RCVBUF SYS_ARCH_DEC(conn->recv_avail, len); #endif /* LWIP_SO_RCVBUF */ diff --git a/components/lwip/api/api_msg.c b/components/lwip/api/api_msg.c index 2d98734b67..878cd289b0 100755 --- a/components/lwip/api/api_msg.c +++ b/components/lwip/api/api_msg.c @@ -202,6 +202,12 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, #endif /* LWIP_NETBUF_RECVINFO */ } +#if ESP_PERF + if (p->len > DBG_PERF_FILTER_LEN) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_LWIP_OUT); + } +#endif + len = p->tot_len; if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { ESP_STATS_INC(esp.rx_udpmbox_post_fail); diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index 4acf518cc9..7335246eb9 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -1437,7 +1437,9 @@ lwip_sendto(int s, const void *data, size_t size, int flags, #else /* LWIP_NETIF_TX_SINGLE_PBUF */ err = netbuf_ref(&buf, data, short_size); #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (err == ERR_OK) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_TX, DBG_PERF_POINT_SOC_OUT); /* send the data */ err = netconn_send(sock->conn, &buf); } diff --git a/components/lwip/api/tcpip.c b/components/lwip/api/tcpip.c index 72de714e2a..f3422af706 100755 --- a/components/lwip/api/tcpip.c +++ b/components/lwip/api/tcpip.c @@ -219,6 +219,12 @@ tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn) msg->msg.inp.p = p; msg->msg.inp.netif = inp; msg->msg.inp.input_fn = input_fn; +#if ESP_PERF + if (p->len > DBG_PERF_FILTER_LEN) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_WIFI_OUT); + } +#endif + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { ESP_STATS_INC(esp.tcpip_inpkt_post_fail); memp_free(MEMP_TCPIP_MSG_INPKT, msg); @@ -492,20 +498,11 @@ tcpip_init(tcpip_init_done_fn initfunc, void *arg) #endif /* LWIP_TCPIP_CORE_LOCKING */ -#if ESP_LWIP -#if ESP_DUAL_CORE - sys_thread_t xLwipTaskHandle = 0; - xTaskCreatePinnedToCore(tcpip_thread, TCPIP_THREAD_NAME, TCPIP_THREAD_STACKSIZE, NULL, TCPIP_THREAD_PRIO, NULL, 1); -#else sys_thread_t xLwipTaskHandle = sys_thread_new(TCPIP_THREAD_NAME , tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); -#endif printf("tcpip_task_hdlxxx : %x, prio:%d,stack:%d\n", (u32_t)xLwipTaskHandle,TCPIP_THREAD_PRIO,TCPIP_THREAD_STACKSIZE); -#else - sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); -#endif } diff --git a/components/lwip/apps/ping/esp_ping.c b/components/lwip/apps/ping/esp_ping.c new file mode 100644 index 0000000000..56ab6a985a --- /dev/null +++ b/components/lwip/apps/ping/esp_ping.c @@ -0,0 +1,131 @@ +// Copyright 2015-2016 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. + +#include +#include "esp_ping.h" + +#include "lwip/ip_addr.h" + +typedef struct _ping_option { + ip4_addr_t ping_target; + uint32_t ping_count; + uint32_t ping_rcv_timeout; + uint32_t ping_delay; + uint16_t ping_id; + esp_ping_found_fn ping_res_fn; + esp_ping_found ping_res; + void *ping_reserve; +} ping_option; + +static ping_option ping_option_info[1]; + +esp_err_t esp_ping_set_target(ping_target_id_t opt_id, void *opt_val, uint32_t opt_len) +{ + esp_err_t ret = ESP_OK; + + if (opt_val == NULL) { + return ESP_ERR_PING_INVALID_PARAMS; + } + + switch (opt_id) { + case PING_TARGET_IP_ADDRESS: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + ping_option_info->ping_target.addr = *(uint32_t *)opt_val; + break; + case PING_TARGET_IP_ADDRESS_COUNT: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + ping_option_info->ping_count = *(uint32_t *)opt_val; + break; + case PING_TARGET_RCV_TIMEO: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + ping_option_info->ping_rcv_timeout = (*(uint32_t *)opt_val) * 1000; + break; + case PING_TARGET_DELAY_TIME: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + ping_option_info->ping_delay = (*(uint32_t *)opt_val) * 1000; + break; + case PING_TARGET_ID: + ESP_PING_CHECK_OPTLEN(opt_len, uint16_t); + ping_option_info->ping_id = *(uint16_t *)opt_val; + break; + case PING_TARGET_RES_FN: + ping_option_info->ping_res_fn = opt_val; + break; + default: + ret = ESP_ERR_PING_INVALID_PARAMS; + break; + } + + return ret; +} + +esp_err_t esp_ping_get_target(ping_target_id_t opt_id, void *opt_val, uint32_t opt_len) +{ + esp_err_t ret = ESP_OK; + + if (opt_val == NULL) { + return ESP_ERR_PING_INVALID_PARAMS; + } + + switch (opt_id) { + case PING_TARGET_IP_ADDRESS: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + *(uint32_t *)opt_val = ping_option_info->ping_target.addr; + break; + case PING_TARGET_IP_ADDRESS_COUNT: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + *(uint32_t *)opt_val = ping_option_info->ping_count; + break; + case PING_TARGET_RCV_TIMEO: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + *(uint32_t *)opt_val = ping_option_info->ping_rcv_timeout; + break; + case PING_TARGET_DELAY_TIME: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + *(uint32_t *)opt_val = ping_option_info->ping_delay; + break; + case PING_TARGET_ID: + ESP_PING_CHECK_OPTLEN(opt_len, uint16_t); + *(uint16_t *)opt_val = ping_option_info->ping_id; + break; + default: + ret = ESP_ERR_PING_INVALID_PARAMS; + break; + } + + return ret; +} + +esp_err_t esp_ping_result(uint8_t res_val, uint16_t ping_len, uint32_t ping_time) +{ + esp_err_t ret = ESP_OK; + + ping_option_info->ping_res.bytes = ping_len; + ping_option_info->ping_res.ping_err = res_val; + ping_option_info->ping_res.resp_time = ping_time; + ping_option_info->ping_res.total_time += ping_time; + ping_option_info->ping_res.total_bytes += ping_len; + + if (res_val == 0) { + ping_option_info->ping_res.timeout_count ++; + } + + if (--ping_option_info->ping_count != 0 && ping_option_info->ping_res_fn) { + ping_option_info->ping_res_fn(PING_TARGET_RES_FN, &ping_option_info->ping_res); + } else { + memset(&ping_option_info->ping_res, 0, sizeof(esp_ping_found)); + } + + return ret; +} diff --git a/components/lwip/apps/ping/esp_ping.h b/components/lwip/apps/ping/esp_ping.h new file mode 100644 index 0000000000..ba9bec2d63 --- /dev/null +++ b/components/lwip/apps/ping/esp_ping.h @@ -0,0 +1,94 @@ +// Copyright 2015-2016 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 ESP_PING_H_ +#define ESP_PING_H_ + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_ERR_PING_BASE 0x5000 + +#define ESP_ERR_PING_INVALID_PARAMS ESP_ERR_PING_BASE + 0x00 +#define ESP_ERR_PING_NO_MEM ESP_ERR_PING_BASE + 0x01 + +#define ESP_PING_CHECK_OPTLEN(optlen, opttype) do { if ((optlen) < sizeof(opttype)) { return ESP_ERR_PING_INVALID_PARAMS; }}while(0) + +typedef struct _ping_found { + uint32_t resp_time; + uint32_t timeout_count; + uint32_t bytes; + uint32_t total_bytes; + uint32_t total_time; + int8_t ping_err; +} esp_ping_found; + +typedef enum { + PING_TARGET_IP_ADDRESS = 50, /**< target IP address */ + PING_TARGET_IP_ADDRESS_COUNT = 51, /**< target IP address total counter */ + PING_TARGET_RCV_TIMEO = 52, /**< receive timeout */ + PING_TARGET_DELAY_TIME = 53, /**< delay time */ + PING_TARGET_ID = 54, /**< identifier */ + PING_TARGET_RES_FN = 55 +} ping_target_id_t; + +typedef void (* esp_ping_found_fn)(ping_target_id_t found_id, esp_ping_found *found_val); + +/** + * @brief Set PING function option + * + * @param[in] opt_id: option index, 50 for IP, 51 for COUNT, 52 for RCV TIMEOUT, 53 for DELAY TIME, 54 for ID + * @param[in] opt_val: option parameter + * @param[in] opt_len: option length + * + * @return + * - ESP_OK + * - ESP_ERR_PING_INVALID_PARAMS + */ +esp_err_t esp_ping_set_target(ping_target_id_t opt_id, void *opt_val, uint32_t opt_len); + +/** + * @brief Get PING function option + * + * @param[in] opt_id: option index, 50 for IP, 51 for COUNT, 52 for RCV TIMEOUT, 53 for DELAY TIME, 54 for ID + * @param[in] opt_val: option parameter + * @param[in] opt_len: option length + * + * @return + * - ESP_OK + * - ESP_ERR_PING_INVALID_PARAMS + */ +esp_err_t esp_ping_get_target(ping_target_id_t opt_id, void *opt_val, uint32_t opt_len); + +/** + * @brief Get PING function result action + * + * @param[in] res_val: ping function action, 1 for successful, 0 for fail. + * res_len: response bytes + * res_time: response time + * + * @return + * - ESP_OK + * - ESP_ERR_PING_INVALID_PARAMS + */ +esp_err_t esp_ping_result(uint8_t res_val, uint16_t res_len, uint32_t res_time); + +#ifdef __cplusplus +} +#endif + +#endif /* ESP_PING_H_ */ diff --git a/components/lwip/apps/ping/ping.c b/components/lwip/apps/ping/ping.c new file mode 100644 index 0000000000..014d4c6574 --- /dev/null +++ b/components/lwip/apps/ping/ping.c @@ -0,0 +1,366 @@ +/** + * @file + * Ping sender module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +/** + * This is an example of a "ping" sender (with raw API and socket API). + * It can be used as a start point to maintain opened a network connection, or + * like a network "watchdog" for your device. + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 && LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "ping.h" + +#include "lwip/mem.h" +#include "lwip/raw.h" +#include "lwip/icmp.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/inet_chksum.h" + +#if PING_USE_SOCKETS +#include "lwip/sockets.h" +#include "lwip/inet.h" +#endif /* PING_USE_SOCKETS */ + +#ifdef ESP_LWIP +#include "esp_ping.h" +#include "lwip/ip_addr.h" +#endif +/** + * PING_DEBUG: Enable debugging for PING. + */ +#ifndef PING_DEBUG +#define PING_DEBUG LWIP_DBG_ON +#endif + +/** ping target - should be an "ip4_addr_t" */ +#ifndef PING_TARGET +#define PING_TARGET (netif_default ? *netif_ip4_gw(netif_default) : (*IP4_ADDR_ANY)) +#endif + +/** ping receive timeout - in milliseconds */ +#ifndef PING_RCV_TIMEO +#define PING_RCV_TIMEO 1000 +#endif + +/** ping delay - in milliseconds */ +#ifndef PING_DELAY +#define PING_DELAY 1000 +#endif + +/** ping identifier - must fit on a u16_t */ +#ifndef PING_ID +#define PING_ID 0xAFAF +#endif + +/** ping additional data size to include in the packet */ +#ifndef PING_DATA_SIZE +#define PING_DATA_SIZE 32 +#endif + +/** ping result action - no default action */ +#ifndef PING_RESULT +#define PING_RESULT(ping_ok) +#endif + +/* ping variables */ +static u16_t ping_seq_num; +static u32_t ping_time; +#if !PING_USE_SOCKETS +static struct raw_pcb *ping_pcb; +#endif /* PING_USE_SOCKETS */ + +/** Prepare a echo ICMP request */ +static void +ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len) +{ + size_t i; + size_t data_len = len - sizeof(struct icmp_echo_hdr); + + ICMPH_TYPE_SET(iecho, ICMP_ECHO); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + iecho->id = PING_ID; + iecho->seqno = lwip_htons(++ping_seq_num); + + /* fill the additional data buffer with some data */ + for(i = 0; i < data_len; i++) { + ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; + } + + iecho->chksum = inet_chksum(iecho, len); +} + +#if PING_USE_SOCKETS + +/* Ping using the socket ip */ +static err_t +ping_send(int s, ip_addr_t *addr) +{ + int err; + struct icmp_echo_hdr *iecho; + struct sockaddr_in to; + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; + LWIP_ASSERT("ping_size is too big", ping_size <= 0xffff); + LWIP_ASSERT("ping: expect IPv4 address", !IP_IS_V6(addr)); + + iecho = (struct icmp_echo_hdr *)mem_malloc((mem_size_t)ping_size); + if (!iecho) { + return ERR_MEM; + } + + ping_prepare_echo(iecho, (u16_t)ping_size); + + to.sin_len = sizeof(to); + to.sin_family = AF_INET; + inet_addr_from_ipaddr(&to.sin_addr, ip_2_ip4(addr)); + + err = lwip_sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to)); + + mem_free(iecho); + + return (err ? ERR_OK : ERR_VAL); +} + +static void +ping_recv(int s) +{ + char buf[64]; + int len; + struct sockaddr_in from; + struct ip_hdr *iphdr; + struct icmp_echo_hdr *iecho; + int fromlen = sizeof(from); + + while((len = lwip_recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) { + if (len >= (int)(sizeof(struct ip_hdr)+sizeof(struct icmp_echo_hdr))) { + if (from.sin_family != AF_INET) { + /* Ping is not IPv4 */ + LWIP_DEBUGF( PING_DEBUG, ("ping: invalid sin_family\n")); + } else { + ip4_addr_t fromaddr; + inet_addr_to_ipaddr(&fromaddr, &from.sin_addr); + LWIP_DEBUGF( PING_DEBUG, ("ping: recv ")); + ip4_addr_debug_print(PING_DEBUG, &fromaddr); + LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now() - ping_time))); + + iphdr = (struct ip_hdr *)buf; + iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4)); + if ((iecho->id == PING_ID) && (iecho->seqno == lwip_htons(ping_seq_num))) { + /* do some ping result processing */ +#ifdef ESP_LWIP + esp_ping_result((ICMPH_TYPE(iecho) == ICMP_ER), len, (sys_now() - ping_time)); +#else + PING_RESULT((ICMPH_TYPE(iecho) == ICMP_ER)); +#endif + return; + } else { + LWIP_DEBUGF( PING_DEBUG, ("ping: drop\n")); + } + } + } + fromlen = sizeof(from); + } + + if (len == 0) { + LWIP_DEBUGF( PING_DEBUG, ("ping: recv - %"U32_F" ms - timeout\n", (sys_now()-ping_time))); + } + + /* do some ping result processing */ +#ifdef ESP_LWIP + esp_ping_result(0, len, (sys_now()-ping_time)); +#else + PING_RESULT(0); +#endif +} + +static void +ping_thread(void *arg) +{ + int s; + int ret; + ip_addr_t ping_target; +#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD + int timeout = PING_RCV_TIMEO; +#else + struct timeval timeout; + timeout.tv_sec = PING_RCV_TIMEO/1000; + timeout.tv_usec = (PING_RCV_TIMEO%1000)*1000; +#endif + LWIP_UNUSED_ARG(arg); + + if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) { + return; + } + + ret = lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + LWIP_ASSERT("setting receive timeout failed", ret == 0); + LWIP_UNUSED_ARG(ret); + + while (1) { +#ifdef ESP_LWIP + ip4_addr_t ipaddr; + esp_ping_get_target(PING_TARGET_IP_ADDRESS, &ipaddr.addr, sizeof(uint32_t)); + ip_addr_copy_from_ip4(ping_target, ipaddr); +#else + ip_addr_copy_from_ip4(ping_target, PING_TARGET); +#endif + + if (ping_send(s, &ping_target) == ERR_OK) { + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, &ping_target); + LWIP_DEBUGF( PING_DEBUG, ("\n")); + + ping_time = sys_now(); + ping_recv(s); + } else { + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, &ping_target); + LWIP_DEBUGF( PING_DEBUG, (" - error\n")); + } + sys_msleep(PING_DELAY); + } +} + +#else /* PING_USE_SOCKETS */ + +/* Ping using the raw ip */ +static u8_t +ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) +{ + struct icmp_echo_hdr *iecho; + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_ASSERT("p != NULL", p != NULL); + + if ((p->tot_len >= (PBUF_IP_HLEN + sizeof(struct icmp_echo_hdr))) && + pbuf_header(p, -PBUF_IP_HLEN) == 0) { + iecho = (struct icmp_echo_hdr *)p->payload; + + if ((iecho->id == PING_ID) && (iecho->seqno == lwip_htons(ping_seq_num))) { + LWIP_DEBUGF( PING_DEBUG, ("ping: recv ")); + ip_addr_debug_print(PING_DEBUG, addr); + LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now()-ping_time))); + + /* do some ping result processing */ + PING_RESULT(1); + pbuf_free(p); + return 1; /* eat the packet */ + } + /* not eaten, restore original packet */ + pbuf_header(p, PBUF_IP_HLEN); + } + + return 0; /* don't eat the packet */ +} + +static void +ping_send(struct raw_pcb *raw, ip_addr_t *addr) +{ + struct pbuf *p; + struct icmp_echo_hdr *iecho; + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; + + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, addr); + LWIP_DEBUGF( PING_DEBUG, ("\n")); + LWIP_ASSERT("ping_size <= 0xffff", ping_size <= 0xffff); + + p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM); + if (!p) { + return; + } + if ((p->len == p->tot_len) && (p->next == NULL)) { + iecho = (struct icmp_echo_hdr *)p->payload; + + ping_prepare_echo(iecho, (u16_t)ping_size); + + raw_sendto(raw, p, addr); + ping_time = sys_now(); + } + pbuf_free(p); +} + +static void +ping_timeout(void *arg) +{ + struct raw_pcb *pcb = (struct raw_pcb*)arg; + ip_addr_t ping_target; + + LWIP_ASSERT("ping_timeout: no pcb given!", pcb != NULL); + + ip_addr_copy_from_ip4(ping_target, PING_TARGET); + ping_send(pcb, &ping_target); + + sys_timeout(PING_DELAY, ping_timeout, pcb); +} + +static void +ping_raw_init(void) +{ + ping_pcb = raw_new(IP_PROTO_ICMP); + LWIP_ASSERT("ping_pcb != NULL", ping_pcb != NULL); + + raw_recv(ping_pcb, ping_recv, NULL); + raw_bind(ping_pcb, IP_ADDR_ANY); + sys_timeout(PING_DELAY, ping_timeout, ping_pcb); +} + +void +ping_send_now(void) +{ + ip_addr_t ping_target; + LWIP_ASSERT("ping_pcb != NULL", ping_pcb != NULL); + ip_addr_copy_from_ip4(ping_target, PING_TARGET); + ping_send(ping_pcb, &ping_target); +} + +#endif /* PING_USE_SOCKETS */ + +void +ping_init(void) +{ +#if PING_USE_SOCKETS + sys_thread_new("ping_thread", ping_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); +#else /* PING_USE_SOCKETS */ + ping_raw_init(); +#endif /* PING_USE_SOCKETS */ +} + +#endif /* LWIP_IPV4 && LWIP_RAW */ diff --git a/components/lwip/apps/ping/ping.h b/components/lwip/apps/ping/ping.h new file mode 100644 index 0000000000..fa2f7aae16 --- /dev/null +++ b/components/lwip/apps/ping/ping.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef LWIP_PING_H +#define LWIP_PING_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * PING_USE_SOCKETS: Set to 1 to use sockets, otherwise the raw api is used + */ +#ifndef PING_USE_SOCKETS +#define PING_USE_SOCKETS LWIP_SOCKET +#endif + + +void ping_init(void); + +#if !PING_USE_SOCKETS +void ping_send_now(void); +#endif /* !PING_USE_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_PING_H */ diff --git a/components/lwip/component.mk b/components/lwip/component.mk index df116f1a06..9444d229e8 100644 --- a/components/lwip/component.mk +++ b/components/lwip/component.mk @@ -2,9 +2,9 @@ # Component Makefile # -COMPONENT_ADD_INCLUDEDIRS := include/lwip include/lwip/port include/lwip/posix +COMPONENT_ADD_INCLUDEDIRS := include/lwip include/lwip/port include/lwip/posix apps/ping -COMPONENT_SRCDIRS := api apps/sntp apps core/ipv4 core/ipv6 core netif port/freertos port/netif port +COMPONENT_SRCDIRS := api apps/sntp apps/ping apps core/ipv4 core/ipv6 core netif port/freertos port/netif port CFLAGS += -Wno-address # lots of LWIP source files evaluate macros that check address of stack variables diff --git a/components/lwip/core/ipv4/autoip.c b/components/lwip/core/ipv4/autoip.c index faac4957ca..f27d28aa24 100755 --- a/components/lwip/core/ipv4/autoip.c +++ b/components/lwip/core/ipv4/autoip.c @@ -273,7 +273,7 @@ autoip_bind(struct netif *netif) #if ESP_LWIP struct dhcp *dhcp = netif->dhcp; if (dhcp->cb != NULL) { - dhcp->cb(); + dhcp->cb(netif); } #endif return ERR_OK; diff --git a/components/lwip/include/lwip/lwip/opt.h b/components/lwip/include/lwip/lwip/opt.h index c42f3cd735..4d8d6bf70c 100755 --- a/components/lwip/include/lwip/lwip/opt.h +++ b/components/lwip/include/lwip/lwip/opt.h @@ -1665,7 +1665,7 @@ * LWIP_STATS==1: Enable statistics collection in lwip_stats. */ #ifndef LWIP_STATS -#define LWIP_STATS 1 +#define LWIP_STATS 0 #endif #if LWIP_STATS diff --git a/components/lwip/include/lwip/port/arpa/inet.h b/components/lwip/include/lwip/port/arpa/inet.h new file mode 100644 index 0000000000..94c6c17ed5 --- /dev/null +++ b/components/lwip/include/lwip/port/arpa/inet.h @@ -0,0 +1,20 @@ +// Copyright 2015-2016 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 INET_H_ +#define INET_H_ + +#include "lwip/inet.h" + +#endif /* INET_H_ */ diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h old mode 100755 new mode 100644 index 8612eb11b0..6d1dfbd497 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -154,14 +154,14 @@ * this option does not affect outgoing packet sizes, which can be controlled * via IP_FRAG. */ -#define IP_REASSEMBLY 0 +#define IP_REASSEMBLY CONFIG_LWIP_IP_REASSEMBLY /** * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note * that this option does not affect incoming packet sizes, which can be * controlled via IP_REASSEMBLY. */ -#define IP_FRAG 0 +#define IP_FRAG CONFIG_LWIP_IP_FRAG /** * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) @@ -189,6 +189,10 @@ ---------- RAW options ---------- --------------------------------- */ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#define LWIP_RAW 1 /* ---------------------------------- @@ -402,6 +406,27 @@ */ #define DEFAULT_ACCEPTMBOX_SIZE 6 +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define DEFAULT_THREAD_STACKSIZE TCPIP_THREAD_STACKSIZE + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define DEFAULT_THREAD_PRIO TCPIP_THREAD_PRIO + +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_RAW_RECVMBOX_SIZE 6 + /* ---------------------------------------------- ---------- Sequential layer options ---------- @@ -440,7 +465,7 @@ /** * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. */ -#define LWIP_SO_RCVBUF 0 +#define LWIP_SO_RCVBUF CONFIG_LWIP_SO_RCVBUF /** * SO_REUSE==1: Enable SO_REUSEADDR option. @@ -572,21 +597,37 @@ #define ESP_LIGHT_SLEEP 1 #define ESP_L2_TO_L3_COPY CONFIG_L2_TO_L3_COPY #define ESP_CNT_DEBUG 0 -#define ESP_DUAL_CORE 0 -#define TCP_WND_DEFAULT (4*TCP_MSS) -#define TCP_SND_BUF_DEFAULT (2*TCP_MSS) +#define TCP_WND_DEFAULT (4*TCP_MSS) +#define TCP_SND_BUF_DEFAULT (2*TCP_MSS) + +#if ESP_PERF +#define DBG_PERF_PATH_SET(dir, point) +#define DBG_PERF_FILTER_LEN 1000 + +enum { + DBG_PERF_DIR_RX = 0, + DBG_PERF_DIR_TX, +}; + +enum { + DBG_PERF_POINT_INT = 0, + DBG_PERF_POINT_WIFI_IN = 1, + DBG_PERF_POINT_WIFI_OUT = 2, + DBG_PERF_POINT_LWIP_IN = 3, + DBG_PERF_POINT_LWIP_OUT = 4, + DBG_PERF_POINT_SOC_IN = 5, + DBG_PERF_POINT_SOC_OUT = 6, +}; + +#else +#define DBG_PERF_PATH_SET(dir, point) +#define DBG_PERF_FILTER_LEN 1000 +#endif #if ESP_PER_SOC_TCP_WND -#define TCP_WND(pcb) (pcb->per_soc_tcp_wnd) -#define TCP_SND_BUF(pcb) (pcb->per_soc_tcp_snd_buf) -#else -#if ESP_PERF -extern unsigned char misc_prof_get_tcpw(void); -extern unsigned char misc_prof_get_tcp_snd_buf(void); -#define TCP_WND(pcb) (misc_prof_get_tcpw()*TCP_MSS) -#define TCP_SND_BUF(pcb) (misc_prof_get_tcp_snd_buf()*TCP_MSS) -#endif +#define TCP_WND(pcb) (pcb->per_soc_tcp_wnd) +#define TCP_SND_BUF(pcb) (pcb->per_soc_tcp_snd_buf) #endif /** @@ -595,6 +636,7 @@ extern unsigned char misc_prof_get_tcp_snd_buf(void); #define DHCP_DEBUG LWIP_DBG_OFF #define LWIP_DEBUG LWIP_DBG_OFF #define TCP_DEBUG LWIP_DBG_OFF +#define ESP_STATS 0 #define CHECKSUM_CHECK_UDP 0 #define CHECKSUM_CHECK_IP 0 diff --git a/components/lwip/include/lwip/port/netinet/in.h b/components/lwip/include/lwip/port/netinet/in.h new file mode 100644 index 0000000000..7eaec63342 --- /dev/null +++ b/components/lwip/include/lwip/port/netinet/in.h @@ -0,0 +1,22 @@ +// Copyright 2015-2016 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 IN_H_ +#define IN_H_ + +#include "lwip/inet.h" + +#define IN6_IS_ADDR_MULTICAST(a) IN_MULTICAST(a) + +#endif /* IN_H_ */ diff --git a/components/lwip/netif/ethernet.c b/components/lwip/netif/ethernet.c index 6d843913da..90f62583bc 100755 --- a/components/lwip/netif/ethernet.c +++ b/components/lwip/netif/ethernet.c @@ -72,6 +72,12 @@ ethernet_input(struct pbuf *p, struct netif *netif) s16_t ip_hdr_offset = SIZEOF_ETH_HDR; #endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ +#if ESP_PERF + if (p->len > DBG_PERF_FILTER_LEN) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_LWIP_IN); + } +#endif + if (p->len <= SIZEOF_ETH_HDR) { /* a packet with only an ethernet header (or less) is not valid for us */ ETHARP_STATS_INC(etharp.proterr); diff --git a/components/lwip/port/freertos/sys_arch.c b/components/lwip/port/freertos/sys_arch.c index 97ee32d1bd..7f96c9b859 100755 --- a/components/lwip/port/freertos/sys_arch.c +++ b/components/lwip/port/freertos/sys_arch.c @@ -164,9 +164,9 @@ sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) StartTime = xTaskGetTickCount(); if (timeout != 0) { - if (xSemaphoreTake(*sem, timeout / portTICK_RATE_MS) == pdTRUE) { + if (xSemaphoreTake(*sem, timeout / portTICK_PERIOD_MS) == pdTRUE) { EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -180,7 +180,7 @@ sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) while (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE); EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -293,9 +293,9 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) sys_mutex_lock(&(*mbox)->lock); if (timeout != 0) { - if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), timeout / portTICK_RATE_MS)) { + if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), timeout / portTICK_PERIOD_MS)) { EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -323,7 +323,7 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) } EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -566,7 +566,7 @@ void sys_thread_sem_deinit(void) void sys_delay_ms(uint32_t ms) { - vTaskDelay(ms/portTICK_RATE_MS); + vTaskDelay(ms / portTICK_PERIOD_MS); } diff --git a/components/lwip/port/netif/ethernetif.c b/components/lwip/port/netif/ethernetif.c old mode 100755 new mode 100644 index 79f21f6b3f..90a5b241b0 --- a/components/lwip/port/netif/ethernetif.c +++ b/components/lwip/port/netif/ethernetif.c @@ -55,11 +55,6 @@ #define IFNAME0 'e' #define IFNAME1 'n' -static char hostname[16]; -#if ESP_PERF -uint32_t g_rx_alloc_pbuf_fail_cnt = 0; -#endif - /** * In this function, the hardware should be initialized. * Called from ethernetif_init(). @@ -81,14 +76,13 @@ ethernet_low_level_init(struct netif *netif) /* device capabilities */ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; - + #if ESP_LWIP #if LWIP_IGMP - - netif->flags |= NETIF_FLAG_IGMP; + netif->flags |= NETIF_FLAG_IGMP; #endif #endif - /* Do whatever else is needed to initialize interface. */ + /* Do whatever else is needed to initialize interface. */ } /** @@ -116,30 +110,28 @@ ethernet_low_level_output(struct netif *netif, struct pbuf *p) LWIP_DEBUGF(NETIF_DEBUG,("eth_if=%d netif=%p pbuf=%p len=%d\n", eth_if, netif, p, p->len)); return ERR_IF; - } - + } + #if ESP_LWIP - q = p; - u16_t pbuf_x_len = 0; - pbuf_x_len = q->len; - if(q->next !=NULL) - { - //char cnt = 0; - struct pbuf *tmp = q->next; - while(tmp != NULL) - { - memcpy( (u8_t *)( (u8_t *)(q->payload) + pbuf_x_len), (u8_t *)tmp->payload , tmp->len ); - pbuf_x_len += tmp->len; - //cnt++; - tmp = tmp->next; - } + q = p; + u16_t pbuf_x_len = 0; + pbuf_x_len = q->len; + if(q->next !=NULL) { + //char cnt = 0; + struct pbuf *tmp = q->next; + while(tmp != NULL) { + memcpy( (u8_t *)( (u8_t *)(q->payload) + pbuf_x_len), (u8_t *)tmp->payload , tmp->len ); + pbuf_x_len += tmp->len; + //cnt++; + tmp = tmp->next; } - - return esp_eth_tx(q->payload, pbuf_x_len); + } + + return esp_eth_tx(q->payload, pbuf_x_len); #else - for(q = p; q != NULL; q = q->next) { - return esp_emac_tx(q->payload, q->len); - } + for(q = p; q != NULL; q = q->next) { + return esp_emac_tx(q->payload, q->len); + } return ERR_OK; #endif } @@ -157,13 +149,12 @@ void ethernetif_input(struct netif *netif, void *buffer, uint16_t len) { struct pbuf *p; - + if(buffer== NULL || netif == NULL) - goto _exit; + goto _exit; #if CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); if (p == NULL) { - //g_rx_alloc_pbuf_fail_cnt++; return; } memcpy(p->payload, buffer, len); @@ -182,7 +173,7 @@ if (netif->input(p, netif) != ERR_OK) { p->payload = buffer; p->user_flag = PBUF_USER_FLAG_OWNER_ETH; p->user_buf = buffer; - + /* full packet send to tcpip_thread to process */ if (netif->input(p, netif) != ERR_OK) { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); @@ -191,7 +182,7 @@ if (netif->input(p, netif) != ERR_OK) { } #endif _exit: -; +; } /** @@ -215,14 +206,11 @@ ethernetif_init(struct netif *netif) /* Initialize interface hostname */ #if ESP_LWIP - sprintf(hostname, "ESP_%02X%02X%02X", netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]); - netif->hostname = hostname; - + netif->hostname = "espressif"; #else - sprintf(hostname, "ESP_%02X%02X%02X", netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]); - netif->hostname = hostname; + netif->hostname = "lwip"; #endif - + #endif /* LWIP_NETIF_HOSTNAME */ /* @@ -243,7 +231,7 @@ ethernetif_init(struct netif *netif) netif->output_ip6 = ethip6_output; #endif /* LWIP_IPV6 */ netif->linkoutput = ethernet_low_level_output; - + /* initialize the hardware */ ethernet_low_level_init(netif); diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c old mode 100755 new mode 100644 index fea163f8cc..f9def49218 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -56,11 +56,6 @@ #define IFNAME0 'e' #define IFNAME1 'n' -static char hostname[16]; -#if ESP_PERF -uint32_t g_rx_alloc_pbuf_fail_cnt = 0; -#endif - /** * In this function, the hardware should be initialized. * Called from ethernetif_init(). @@ -70,11 +65,7 @@ uint32_t g_rx_alloc_pbuf_fail_cnt = 0; */ static void low_level_init(struct netif *netif) -{ - - - - +{ /* set MAC hardware address length */ netif->hwaddr_len = ETHARP_HWADDR_LEN; @@ -86,18 +77,14 @@ low_level_init(struct netif *netif) /* device capabilities */ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; - + #if ESP_LWIP - #if LWIP_IGMP - - netif->flags |= NETIF_FLAG_IGMP; + netif->flags |= NETIF_FLAG_IGMP; +#endif #endif - - #endif - - /* Do whatever else is needed to initialize interface. */ + /* Do whatever else is needed to initialize interface. */ } /** @@ -118,29 +105,29 @@ low_level_init(struct netif *netif) static err_t low_level_output(struct netif *netif, struct pbuf *p) { - wifi_interface_t wifi_if = tcpip_adapter_get_esp_if(netif); - struct pbuf *q = p; - err_t ret; + wifi_interface_t wifi_if = tcpip_adapter_get_esp_if(netif); + struct pbuf *q = p; + err_t ret; - if (wifi_if >= ESP_IF_MAX) { - return ERR_IF; - } + if (wifi_if >= ESP_IF_MAX) { + return ERR_IF; + } - if(q->next == NULL) { - ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); + if(q->next == NULL) { + ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); + } else { + LWIP_DEBUGF(PBUF_DEBUG, ("low_level_output: pbuf is a list, application may has bug")); + q = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM); + if (q != NULL) { + pbuf_copy(q, p); } else { - LWIP_DEBUGF(PBUF_DEBUG, ("low_level_output: pbuf is a list, application may has bug")); - q = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM); - if (q != NULL) { - pbuf_copy(q, p); - } else { - return ERR_MEM; - } - ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); - pbuf_free(q); + return ERR_MEM; } - - return ret; + ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); + pbuf_free(q); + } + + return ret; } /** @@ -156,9 +143,9 @@ void wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) { struct pbuf *p; - + if(!buffer || !netif) - goto _exit; + goto _exit; #if (ESP_L2_TO_L3_COPY == 1) p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); @@ -185,9 +172,9 @@ wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); pbuf_free(p); } - + _exit: -; +; } /** @@ -211,25 +198,11 @@ wlanif_init(struct netif *netif) /* Initialize interface hostname */ #if ESP_LWIP -//TO_DO -/* - if ((struct netif *)wifi_get_netif(STATION_IF) == netif) { - if (default_hostname == 1) { - wifi_station_set_default_hostname(netif->hwaddr); - } - netif->hostname = hostname; - } else { - netif->hostname = NULL; - } -*/ - sprintf(hostname, "ESP_%02X%02X%02X", netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]); - netif->hostname = hostname; - + netif->hostname = "espressif"; #else - sprintf(hostname, "ESP_%02X%02X%02X", netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]); - netif->hostname = hostname; + netif->hostname = "lwip"; #endif - + #endif /* LWIP_NETIF_HOSTNAME */ /* @@ -250,7 +223,7 @@ wlanif_init(struct netif *netif) netif->output_ip6 = ethip6_output; #endif /* LWIP_IPV6 */ netif->linkoutput = low_level_output; - + /* initialize the hardware */ low_level_init(netif); diff --git a/components/mbedtls/test/test_mbedtls_sha.c b/components/mbedtls/test/test_mbedtls_sha.c index 721c59b761..b18169827b 100644 --- a/components/mbedtls/test/test_mbedtls_sha.c +++ b/components/mbedtls/test/test_mbedtls_sha.c @@ -166,7 +166,7 @@ TEST_CASE("mbedtls SHA self-tests multithreaded", "[mbedtls]") xTaskCreate(tskRunSHASelftests, "SHASelftests2", 8192, NULL, 3, NULL); for(int i = 0; i < 2; i++) { - if(!xSemaphoreTake(done_sem, 10000/portTICK_PERIOD_MS)) { + if(!xSemaphoreTake(done_sem, 12000/portTICK_PERIOD_MS)) { TEST_FAIL_MESSAGE("done_sem not released by test task"); } } diff --git a/components/mdns/component.mk b/components/mdns/component.mk new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/mdns/include/mdns.h b/components/mdns/include/mdns.h new file mode 100644 index 0000000000..58e588e3e6 --- /dev/null +++ b/components/mdns/include/mdns.h @@ -0,0 +1,235 @@ +// Copyright 2015-2016 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 ESP_MDNS_H_ +#define ESP_MDNS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct mdns_server_s; +typedef struct mdns_server_s mdns_server_t; + +/** + * @brief mDNS query result structure + * + */ +typedef struct mdns_result_s { + const char * host; /*!< hostname */ + const char * instance; /*!< instance */ + const char * txt; /*!< txt data */ + uint16_t priority; /*!< service priority */ + uint16_t weight; /*!< service weight */ + uint16_t port; /*!< service port */ + struct ip4_addr addr; /*!< ip4 address */ + struct ip6_addr addrv6; /*!< ip6 address */ + const struct mdns_result_s * next; /*!< next result, or NULL for the last result in the list */ +} mdns_result_t; + +/** + * @brief Initialize mDNS on given interface + * + * @param tcpip_if Interface that the server will listen on + * @param server Server pointer to populate on success + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG when bad tcpip_if is given + * - ESP_ERR_INVALID_STATE when the network returned error + * - ESP_ERR_NO_MEM on memory error + * - ESP_ERR_WIFI_NOT_INIT when WiFi is not initialized by eps_wifi_init + */ +esp_err_t mdns_init(tcpip_adapter_if_t tcpip_if, mdns_server_t ** server); + +/** + * @brief Stop and free mDNS server + * + * @param server mDNS Server to free + * + */ +void mdns_free(mdns_server_t * server); + +/** + * @brief Set the hostname for mDNS server + * + * @param server mDNS Server + * @param hostname Hostname to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_set_hostname(mdns_server_t * server, const char * hostname); + +/** + * @brief Set the default instance name for mDNS server + * + * @param server mDNS Server + * @param instance Instance name to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_set_instance(mdns_server_t * server, const char * instance); + +/** + * @brief Add service to mDNS server + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param port service port + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_add(mdns_server_t * server, const char * service, const char * proto, uint16_t port); + +/** + * @brief Remove service from mDNS server + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_FAIL unknown error + */ +esp_err_t mdns_service_remove(mdns_server_t * server, const char * service, const char * proto); + +/** + * @brief Set instance name for service + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param instance instance name to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_instance_set(mdns_server_t * server, const char * service, const char * proto, const char * instance); + +/** + * @brief Set TXT data for service + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param num_items number of items in TXT data + * @param txt string array of TXT data (eg. {"var=val","other=2"}) + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_set(mdns_server_t * server, const char * service, const char * proto, uint8_t num_items, const char ** txt); + +/** + * @brief Set service port + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param port service port + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + */ +esp_err_t mdns_service_port_set(mdns_server_t * server, const char * service, const char * proto, uint16_t port); + +/** + * @brief Remove and free all services from mDNS server + * + * @param server mDNS Server + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t mdns_service_remove_all(mdns_server_t * server); + +/** + * @brief Query mDNS for host or service + * + * @param server mDNS Server + * @param service service type or host name + * @param proto service protocol or NULL if searching for host + * @param timeout time to wait for answers. If 0, mdns_query_end MUST be called to end the search + * + * @return the number of results found + */ +size_t mdns_query(mdns_server_t * server, const char * service, const char * proto, uint32_t timeout); + +/** + * @brief Stop mDNS Query started with timeout = 0 + * + * @param server mDNS Server + * + * @return the number of results found + */ +size_t mdns_query_end(mdns_server_t * server); + +/** + * @brief get the number of results currently in memoty + * + * @param server mDNS Server + * + * @return the number of results + */ +size_t mdns_result_get_count(mdns_server_t * server); + +/** + * @brief Get mDNS Search result with given index + * + * @param server mDNS Server + * @param num the index of the result + * + * @return the result or NULL if error + */ +const mdns_result_t * mdns_result_get(mdns_server_t * server, size_t num); + +/** + * @brief Remove and free all search results from mDNS server + * + * @param server mDNS Server + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t mdns_result_free(mdns_server_t * server); + +#ifdef __cplusplus +} +#endif + +#endif /* ESP_MDNS_H_ */ diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c new file mode 100644 index 0000000000..d636134c7d --- /dev/null +++ b/components/mdns/mdns.c @@ -0,0 +1,1861 @@ +// Copyright 2015-2016 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. +#include "mdns.h" + +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "lwip/ip_addr.h" +#include "lwip/pbuf.h" +#include "lwip/igmp.h" +#include "lwip/udp.h" +#include "esp_wifi.h" + +#define MDNS_FLAGS_AUTHORITATIVE 0x8400 + +#define MDNS_NAME_REF 0xC000 + +#define MDNS_TYPE_AAAA 0x001C +#define MDNS_TYPE_A 0x0001 +#define MDNS_TYPE_PTR 0x000C +#define MDNS_TYPE_SRV 0x0021 +#define MDNS_TYPE_TXT 0x0010 +#define MDNS_TYPE_NSEC 0x002F +#define MDNS_TYPE_ANY 0x00FF + +#define MDNS_CLASS_IN 0x0001 +#define MDNS_CLASS_IN_FLUSH_CACHE 0x8001 + +#define MDNS_ANSWER_ALL 0x3F +#define MDNS_ANSWER_PTR 0x08 +#define MDNS_ANSWER_TXT 0x04 +#define MDNS_ANSWER_SRV 0x02 +#define MDNS_ANSWER_A 0x01 +#define MDNS_ANSWER_AAAA 0x10 +#define MDNS_ANSWER_NSEC 0x20 + +#define MDNS_SERVICE_PORT 5353 // UDP port that the server runs on +#define MDNS_SERVICE_STACK_DEPTH 4096 // Stack size for the service thread +#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing +#define MDNS_TXT_MAX_LEN 1024 // Maximum string length of text data in TXT record +#define MDNS_NAME_MAX_LEN 64 // Maximum string length of hostname, instance, service and proto +#define MDNS_NAME_BUF_LEN (MDNS_NAME_MAX_LEN+1) // Maximum char buffer size to hold hostname, instance, service or proto +#define MDNS_MAX_PACKET_SIZE 1460 // Maximum size of mDNS outgoing packet + +#define MDNS_ANSWER_PTR_TTL 4500 +#define MDNS_ANSWER_TXT_TTL 4500 +#define MDNS_ANSWER_SRV_TTL 120 +#define MDNS_ANSWER_A_TTL 120 +#define MDNS_ANSWER_AAAA_TTL 120 + +#define MDNS_HEAD_LEN 12 +#define MDNS_HEAD_ID_OFFSET 0 +#define MDNS_HEAD_FLAGS_OFFSET 2 +#define MDNS_HEAD_QUESTIONS_OFFSET 4 +#define MDNS_HEAD_ANSWERS_OFFSET 6 +#define MDNS_HEAD_SERVERS_OFFSET 8 +#define MDNS_HEAD_ADDITIONAL_OFFSET 10 + +#define MDNS_TYPE_OFFSET 0 +#define MDNS_CLASS_OFFSET 2 +#define MDNS_TTL_OFFSET 4 +#define MDNS_LEN_OFFSET 8 +#define MDNS_DATA_OFFSET 10 + +#define MDNS_SRV_PRIORITY_OFFSET 0 +#define MDNS_SRV_WEIGHT_OFFSET 2 +#define MDNS_SRV_PORT_OFFSET 4 +#define MDNS_SRV_FQDN_OFFSET 6 + +typedef struct { + char host[MDNS_NAME_BUF_LEN]; + char service[MDNS_NAME_BUF_LEN]; + char proto[MDNS_NAME_BUF_LEN]; + char domain[MDNS_NAME_BUF_LEN]; + uint8_t parts; + uint8_t sub; +} mdns_name_t; + +typedef struct { + char host[MDNS_NAME_BUF_LEN]; + char instance[MDNS_NAME_BUF_LEN]; + char txt[MDNS_TXT_MAX_LEN]; + uint16_t priority; + uint16_t weight; + uint16_t port; + uint32_t addr; + uint8_t addrv6[16]; + uint8_t ptr; +} mdns_result_temp_t; + +typedef struct { + const char * host; + const char * sub; + const char * service; + const char * proto; + const char * domain; + uint8_t parts; + uint8_t done; +} mdns_string_t; + +typedef struct mdns_service_s { + const char * instance; + const char * service; + const char * proto; + uint16_t priority; + uint16_t weight; + uint16_t port; + uint8_t txt_num_items; + const char ** txt; +} mdns_service_t; + +typedef struct mdns_srv_item_s { + mdns_service_t * service; + struct mdns_srv_item_s * next; +} mdns_srv_item_t; + +typedef struct mdns_answer_item_s { + mdns_service_t * service; + uint8_t answer; + struct mdns_answer_item_s * next; +} mdns_answer_item_t; + +struct mdns_server_s { + tcpip_adapter_if_t tcpip_if; + struct udp_pcb * pcb; + const char * hostname; + const char * instance; + mdns_srv_item_t * services; + xSemaphoreHandle lock; + xQueueHandle queue; + struct { + char host[MDNS_NAME_BUF_LEN]; + char service[MDNS_NAME_BUF_LEN]; + char proto[MDNS_NAME_BUF_LEN]; + bool running; + xSemaphoreHandle lock; + mdns_result_t * results; + } search; +}; + +#define MDNS_MUTEX_LOCK() xSemaphoreTake(server->lock, portMAX_DELAY) +#define MDNS_MUTEX_UNLOCK() xSemaphoreGive(server->lock) + +#define MDNS_SEARCH_LOCK() xSemaphoreTake(server->search.lock, portMAX_DELAY) +#define MDNS_SEARCH_UNLOCK() xSemaphoreGive(server->search.lock) + +static const char * MDNS_DEFAULT_DOMAIN = "local"; +static const char * MDNS_SUB_STR = "_sub"; + +static mdns_server_t * _mdns_servers[TCPIP_ADAPTER_IF_MAX] = {0,0,0}; +static TaskHandle_t _mdns_service_task_handle = NULL; +static QueueSetHandle_t _mdns_queue_set = NULL; + +static xSemaphoreHandle _mdns_service_semaphore = NULL; +#define MDNS_SERVICE_LOCK() xSemaphoreTake(_mdns_service_semaphore, portMAX_DELAY) +#define MDNS_SERVICE_UNLOCK() xSemaphoreGive(_mdns_service_semaphore) + +/* + * MDNS Server Networking + * */ + +/** + * @brief the receive callback of the raw udp api. Packets are received here + * + */ +static void _mdns_server_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *addr, uint16_t port) +{ + while(pb != NULL) { + struct pbuf * this_pb = pb; + pb = pb->next; + this_pb->next = NULL; + mdns_server_t * server = (mdns_server_t *)arg; + if (!server || !server->queue || xQueueSend(server->queue, &this_pb, (portTickType)0) != pdPASS) { + pbuf_free(this_pb); + } + } +} + +/** + * @brief init the network of MDNS server + * + * @param server The server + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE on igmp/bind error + * - ESP_ERR_NO_MEM on memory error + */ +esp_err_t _mdns_server_init(mdns_server_t * server) +{ + esp_err_t err = ESP_OK; + + tcpip_adapter_ip_info_t if_ip_info; + err = tcpip_adapter_get_ip_info(server->tcpip_if, &if_ip_info); + if (err) { + return err; + } + + ip_addr_t laddr; + IP_ADDR4(&laddr, 224, 0, 0, 251); + + ip_addr_t multicast_if_addr = IPADDR4_INIT(if_ip_info.ip.addr); + + if (igmp_joingroup((const struct ip4_addr *)&multicast_if_addr.u_addr.ip4, (const struct ip4_addr *)&laddr.u_addr.ip4)) { + return ESP_ERR_INVALID_STATE; + } + + struct udp_pcb * pcb = udp_new(); + if (!pcb) { + return ESP_ERR_NO_MEM; + } + + pcb->remote_port = MDNS_SERVICE_PORT; + + if (udp_bind(pcb, &multicast_if_addr, pcb->remote_port) != 0) { + udp_remove(pcb); + return ESP_ERR_INVALID_STATE; + } + + pcb->mcast_ttl = 1; + ip_addr_copy(pcb->multicast_ip, multicast_if_addr); + ip_addr_copy(pcb->remote_ip, laddr); + + server->pcb = pcb; + udp_recv(pcb, &_mdns_server_recv, server); + return err; +} + +/** + * @brief stop the network of MDNS server + * + * @param server The server + * + * @return ESP_OK + */ +esp_err_t _mdns_server_deinit(mdns_server_t * server) +{ + if (server->pcb) { + udp_recv(server->pcb, NULL, NULL); + udp_disconnect(server->pcb); + udp_remove(server->pcb); + server->pcb = NULL; + } + return ESP_OK; +} + +/** + * @brief send packet over UDP + * + * @param server The server + * @param data byte array containing the packet data + * @param len length of the packet data + * + * @return length of sent packet or 0 on error + */ +static size_t _mdns_server_write(mdns_server_t * server, uint8_t * data, size_t len) +{ + struct pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pbt != NULL) { + uint8_t* dst = (uint8_t *)pbt->payload; + memcpy(dst, data, len); + err_t err = udp_sendto(server->pcb, pbt, &(server->pcb->remote_ip), server->pcb->remote_port); + pbuf_free(pbt); + if (err) { + return 0; + } + return len; + } + return 0; +} + +/* + * MDNS Servers + * */ + +static void _mdns_parse_packet(mdns_server_t * server, const uint8_t * data, size_t len); + +/** + * @brief the main MDNS service task. Packets are received and parsed here + */ +static void _mdns_service_task(void *pvParameters) +{ + uint8_t i; + struct pbuf * pb; + QueueSetMemberHandle_t queue; + + for(;;) { + queue = xQueueSelectFromSet(_mdns_queue_set, portMAX_DELAY); + if (queue && xQueueReceive(queue, &pb, 0) == pdTRUE) { + for(i=0; iqueue == queue) { + MDNS_MUTEX_LOCK(); + _mdns_parse_packet(server, (uint8_t*)pb->payload, pb->len); + MDNS_MUTEX_UNLOCK(); + break; + } + } + pbuf_free(pb); + } + } +} + +/** + * @brief get the server assigned to particular interface + * + * @param tcpip_if The interface + * + * @return reference to the server from the server list or NULL if not found + */ +static mdns_server_t * _mdns_server_get(tcpip_adapter_if_t tcpip_if) +{ + if (tcpip_if < TCPIP_ADAPTER_IF_MAX) { + return _mdns_servers[tcpip_if]; + } + return NULL; +} + +/** + * @brief add server to the server list. Start the service thread if not running + * + * @param server The server to add + * + * @return + * - ESP_OK on success + * - ESP_FAIL on error + * - ESP_ERR_* on network error + */ +static esp_err_t _mdns_server_add(mdns_server_t * server) +{ + if (!_mdns_service_semaphore) { + _mdns_service_semaphore = xSemaphoreCreateMutex(); + if (!_mdns_service_semaphore) { + return ESP_FAIL; + } + } + MDNS_SERVICE_LOCK(); + if (!_mdns_service_task_handle) { + _mdns_queue_set = xQueueCreateSet(TCPIP_ADAPTER_IF_MAX * MDNS_PACKET_QUEUE_LEN); + if (!_mdns_queue_set) { + MDNS_SERVICE_UNLOCK(); + return ESP_FAIL; + } + xTaskCreatePinnedToCore(_mdns_service_task, "mdns", MDNS_SERVICE_STACK_DEPTH, NULL, 1, &_mdns_service_task_handle, 0); + if (!_mdns_service_task_handle) { + vQueueDelete(_mdns_queue_set); + _mdns_queue_set = NULL; + MDNS_SERVICE_UNLOCK(); + return ESP_FAIL; + } + } + MDNS_SERVICE_UNLOCK(); + + if (xQueueAddToSet(server->queue, _mdns_queue_set) != pdPASS) { + return ESP_FAIL; + } + + //start UDP + esp_err_t err = _mdns_server_init(server); + if (err) { + return err; + } + + _mdns_servers[server->tcpip_if] = server; + + return ESP_OK; +} + +/** + * @brief remove server from server list. Stop the service thread in no more servers are running + * + * @param server The server to remove + * + * @return + * - ESP_OK on success + * - ESP_FAIL on error + */ +static esp_err_t _mdns_server_remove(mdns_server_t * server) +{ + //stop UDP + _mdns_server_deinit(server); + + _mdns_servers[server->tcpip_if] = NULL; + + if (xQueueRemoveFromSet(server->queue, _mdns_queue_set) != pdPASS) { + return ESP_FAIL; + } + + uint8_t i; + for(i=0; iservice == service) { + //just add the new answer type to it + a->answer |= type; + return answers; + } + a = a->next; + } + //prepend the q with this new answer + a = (mdns_answer_item_t *)malloc(sizeof(mdns_answer_item_t)); + if (!a) { + return answers;//fail! + } + a->service = service; + a->answer = type; + a->next = answers; + answers = a; + return a; +} + +/** + * @brief reads MDNS FQDN into mdns_name_t structure + * FQDN is in format: [hostname.|[instance.]_service._proto.]local. + * + * @param packet MDNS packet + * @param start Starting point of FQDN + * @param name mdns_name_t structure to populate + * @param buf temporary char buffer + * + * @return the address after the parsed FQDN in the packet or NULL on error + */ +static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * start, mdns_name_t * name, char * buf) +{ + size_t index = 0; + while(start[index]) { + if (name->parts == 4) { + return NULL; + } + uint8_t len = start[index++]; + if ((len & 0xC0) == 0) { + if (len > 64) { + //length can not be more than 64 + return NULL; + } + uint8_t i; + for(i=0; iparts == 1 && buf[0] != '_' + && (strcmp(buf, MDNS_DEFAULT_DOMAIN) != 0) + && (strcmp(buf, "ip6") != 0) + && (strcmp(buf, "in-addr") != 0)) { + sprintf((char*)name, "%s.%s", name->host, buf); + } else if (strcmp(buf, MDNS_SUB_STR) == 0) { + name->sub = 1; + } else { + memcpy((uint8_t*)name + (name->parts++ * (MDNS_NAME_BUF_LEN)), buf, len+1); + } + } else { + size_t address = (((uint16_t)len & 0x3F) << 8) | start[index++]; + if ((packet + address) > start) { + //reference address can not be after where we are + return NULL; + } + if (_mdns_read_fqdn(packet, packet + address, name, buf)) { + return start + index; + } + return NULL; + } + } + return start + index + 1; +} + +/** + * @brief reads and formats MDNS FQDN into mdns_name_t structure + * + * @param packet MDNS packet + * @param start Starting point of FQDN + * @param name mdns_name_t structure to populate + * + * @return the address after the parsed FQDN in the packet or NULL on error + */ +static const uint8_t * _mdns_parse_fqdn(const uint8_t * packet, const uint8_t * start, mdns_name_t * name) +{ + name->parts = 0; + name->sub = 0; + name->host[0] = 0; + name->service[0] = 0; + name->proto[0] = 0; + name->domain[0] = 0; + + static char buf[MDNS_NAME_BUF_LEN]; + + const uint8_t * next_data = (uint8_t*)_mdns_read_fqdn(packet, start, name, buf); + if (!next_data || name->parts < 2) { + return 0; + } + if (name->parts == 3) { + memmove((uint8_t*)name + (MDNS_NAME_BUF_LEN), (uint8_t*)name, 3*(MDNS_NAME_BUF_LEN)); + name->host[0] = 0; + } else if (name->parts == 2) { + memmove((uint8_t*)(name->domain), (uint8_t*)(name->service), (MDNS_NAME_BUF_LEN)); + name->service[0] = 0; + name->proto[0] = 0; + } + if (strcmp(name->domain, MDNS_DEFAULT_DOMAIN) == 0 || strcmp(name->domain, "arpa") == 0) { + return next_data; + } + return 0; +} + +/* + * Packet construction + * */ + +/** + * @brief sets uint16_t value in a packet + * + * @param packet MDNS packet + * @param index offset of uint16_t value + * @param value the value to set + */ +static inline void _mdns_set_u16(uint8_t * packet, uint16_t index, uint16_t value) +{ + if ((index + 1) >= MDNS_MAX_PACKET_SIZE) { + return; + } + packet[index] = (value >> 8) & 0xFF; + packet[index+1] = value & 0xFF; +} + +/** + * @brief appends byte in a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param value the value to set + * + * @return length of added data: 0 on error or 1 on success + */ +static inline uint8_t _mdns_append_u8(uint8_t * packet, uint16_t * index, uint8_t value) +{ + if (*index >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + packet[*index] = value; + *index += 1; + return 1; +} + +/** + * @brief appends uint16_t in a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param value the value to set + * + * @return length of added data: 0 on error or 2 on success + */ +static inline uint8_t _mdns_append_u16(uint8_t * packet, uint16_t * index, uint16_t value) +{ + if ((*index + 1) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, (value >> 8) & 0xFF); + _mdns_append_u8(packet, index, value & 0xFF); + return 2; +} + +/** + * @brief appends uint32_t in a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param value the value to set + * + * @return length of added data: 0 on error or 4 on success + */ +static inline uint8_t _mdns_append_u32(uint8_t * packet, uint16_t * index, uint32_t value) +{ + if ((*index + 3) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, (value >> 24) & 0xFF); + _mdns_append_u8(packet, index, (value >> 16) & 0xFF); + _mdns_append_u8(packet, index, (value >> 8) & 0xFF); + _mdns_append_u8(packet, index, value & 0xFF); + return 4; +} + +/** + * @brief appends answer type, class, ttl and data length to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param type answer type + * @param ttl answer ttl + * + * @return length of added data: 0 on error or 10 on success + */ +static inline uint8_t _mdns_append_type(uint8_t * packet, uint16_t * index, uint8_t type, uint32_t ttl) +{ + if ((*index + 10) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + if (type == MDNS_ANSWER_PTR) { + _mdns_append_u16(packet, index, MDNS_TYPE_PTR); + _mdns_append_u16(packet, index, MDNS_CLASS_IN); + } else if (type == MDNS_ANSWER_TXT) { + _mdns_append_u16(packet, index, MDNS_TYPE_TXT); + _mdns_append_u16(packet, index, MDNS_CLASS_IN_FLUSH_CACHE); + } else if (type == MDNS_ANSWER_SRV) { + _mdns_append_u16(packet, index, MDNS_TYPE_SRV); + _mdns_append_u16(packet, index, MDNS_CLASS_IN_FLUSH_CACHE); + } else if (type == MDNS_ANSWER_A) { + _mdns_append_u16(packet, index, MDNS_TYPE_A); + _mdns_append_u16(packet, index, MDNS_CLASS_IN_FLUSH_CACHE); + } else if (type == MDNS_ANSWER_AAAA) { + _mdns_append_u16(packet, index, MDNS_TYPE_AAAA); + _mdns_append_u16(packet, index, MDNS_CLASS_IN_FLUSH_CACHE); + } else { + return 0; + } + _mdns_append_u32(packet, index, ttl); + _mdns_append_u16(packet, index, 0); + return 10; +} + +/** + * @brief appends single string to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param string the string to append + * + * @return length of added data: 0 on error or length of the string + 1 on success + */ +static inline uint8_t _mdns_append_string(uint8_t * packet, uint16_t * index, const char * string) +{ + uint8_t len = strlen(string); + if ((*index + len + 1) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, len); + memcpy(packet + *index, string, len); + *index += len; + return len + 1; +} + +/** + * @brief appends FQDN to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param strings string array containing the parts of the FQDN + * @param count number of strings in the array + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_fqdn(uint8_t * packet, uint16_t * index, const char * strings[], uint8_t count) +{ + if (!count) { + return _mdns_append_u8(packet, index, 0); + } + mdns_name_t name; + static char buf[MDNS_NAME_BUF_LEN]; + uint8_t len = strlen(strings[0]); + uint8_t * len_location = (uint8_t *)memchr(packet, (char)len, *index); + while(len_location) { + if (memcmp(len_location+1, strings[0], len)) { //not continuing with our string +search_next: + len_location = (uint8_t *)memchr(len_location+1, (char)len, *index - (len_location+1 - packet)); + continue; + } + //read string into name and compare + name.parts = 0; + name.sub = 0; + name.host[0] = 0; + name.service[0] = 0; + name.proto[0] = 0; + name.domain[0] = 0; + const uint8_t * content = _mdns_read_fqdn(packet, len_location, &name, buf); + if (!content) { + return 0; + } + if (name.parts == count) { + uint8_t i; + for(i=0; iinstance)?service->instance + :(server->instance)?server->instance + :server->hostname; + str[1] = service->service; + str[2] = service->proto; + str[3] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str + 1, 3); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_PTR, MDNS_ANSWER_PTR_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + part_length = _mdns_append_fqdn(packet, index, str, 4); + if (!part_length) { + return 0; + } + _mdns_set_u16(packet, data_len_location, part_length); + record_length += part_length; + return record_length; +} + +/** + * @brief appends TXT record for service to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server that is hosting the service + * @param service the service to add record for + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_txt_record(uint8_t * packet, uint16_t * index, mdns_server_t * server, mdns_service_t * service) +{ + const char * str[4]; + uint16_t record_length = 0; + uint8_t part_length; + + str[0] = (service->instance)?service->instance + :(server->instance)?server->instance + :server->hostname; + str[1] = service->service; + str[2] = service->proto; + str[3] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str, 4); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_TXT, MDNS_ANSWER_TXT_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + uint16_t data_len = 0; + if (service->txt_num_items) { + uint8_t len = service->txt_num_items; + const char ** txt = service->txt; + uint8_t i, l; + for(i=0; iinstance)?service->instance + :(server->instance)?server->instance + :server->hostname; + str[1] = service->service; + str[2] = service->proto; + str[3] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str, 4); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_SRV, MDNS_ANSWER_SRV_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + + part_length = 0; + part_length += _mdns_append_u16(packet, index, service->priority); + part_length += _mdns_append_u16(packet, index, service->weight); + part_length += _mdns_append_u16(packet, index, service->port); + if (part_length != 6) { + return 0; + } + + str[0] = server->hostname; + str[1] = MDNS_DEFAULT_DOMAIN; + part_length = _mdns_append_fqdn(packet, index, str, 2); + if (!part_length) { + return 0; + } + _mdns_set_u16(packet, data_len_location, part_length + 6); + + record_length += part_length + 6; + return record_length; +} + +/** + * @brief appends A record to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server + * @param ip the IP address to add + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_a_record(uint8_t * packet, uint16_t * index, mdns_server_t * server, uint32_t ip) +{ + const char * str[2]; + uint16_t record_length = 0; + uint8_t part_length; + + str[0] = server->hostname; + str[1] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str, 2); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_A, MDNS_ANSWER_A_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + + if ((*index + 3) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, ip & 0xFF); + _mdns_append_u8(packet, index, (ip >> 8) & 0xFF); + _mdns_append_u8(packet, index, (ip >> 16) & 0xFF); + _mdns_append_u8(packet, index, (ip >> 24) & 0xFF); + _mdns_set_u16(packet, data_len_location, 4); + + record_length += 4; + return record_length; +} + +/** + * @brief appends AAAA record to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server + * @param ipv6 the IPv6 address to add + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_aaaa_record(uint8_t * packet, uint16_t * index, mdns_server_t * server, uint8_t * ipv6) +{ + const char * str[2]; + uint16_t record_length = 0; + uint8_t part_length; + + str[0] = server->hostname; + str[1] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str, 2); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_AAAA, MDNS_ANSWER_AAAA_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + + if ((*index + 15) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + + part_length = sizeof(ip6_addr_t); + memcpy(packet + *index, ipv6, part_length); + *index += part_length; + _mdns_set_u16(packet, data_len_location, part_length); + record_length += part_length; + return record_length; +} + +/** + * @brief sends all collected answers + * + * @param server the server + * @param answers linked list of answers + */ +static void _mdns_send_answers(mdns_server_t * server, mdns_answer_item_t * answers) +{ + bool send_ip = false; + static uint8_t packet[MDNS_MAX_PACKET_SIZE]; + uint16_t index = MDNS_HEAD_LEN; + uint8_t answer_count = 0; + + memset(packet, 0, MDNS_HEAD_LEN); + + _mdns_set_u16(packet, MDNS_HEAD_FLAGS_OFFSET, MDNS_FLAGS_AUTHORITATIVE); + + while(answers) { + if (answers->answer & MDNS_ANSWER_A) { + answers->answer &= ~MDNS_ANSWER_A; + send_ip = true; + } + if (answers->service) { + + if (answers->answer & MDNS_ANSWER_PTR) { + if (!_mdns_append_ptr_record(packet, &index, server, answers->service)) { + return; + } + answer_count += 1; + } + + if (answers->answer & MDNS_ANSWER_TXT) { + if (!_mdns_append_txt_record(packet, &index, server, answers->service)) { + return; + } + answer_count += 1; + } + + if (answers->answer & MDNS_ANSWER_SRV) { + if (!_mdns_append_srv_record(packet, &index, server, answers->service)) { + return; + } + answer_count += 1; + } + } + mdns_answer_item_t * a = answers; + answers = answers->next; + free(a); + } + if (send_ip) { + tcpip_adapter_ip_info_t if_ip_info; + tcpip_adapter_get_ip_info(server->tcpip_if, &if_ip_info); + + if (!_mdns_append_a_record(packet, &index, server, if_ip_info.ip.addr)) { + return; + } + answer_count += 1; + + //add ipv6 if available + struct ip6_addr if_ip6; + if (!tcpip_adapter_get_ip6_linklocal(server->tcpip_if, &if_ip6)) { + uint8_t * v6addr = (uint8_t*)if_ip6.addr; + //check if not 0 + int i; + for(i=0;ipriority = r->priority; + n->weight = r->weight; + n->port = r->port; + n->addr.addr = r->addr; + + size_t hlen = strlen(r->host); + if (hlen) { + n->host = strdup(r->host); + if (!n->host) { + free(n); + return; + } + } else { + n->host = NULL; + } + + size_t ilen = strlen(r->instance); + if (ilen) { + n->instance = strdup(r->instance); + if (!n->instance) { + free((char *)n->host); + free(n); + return; + } + } else { + n->instance = NULL; + } + + size_t tlen = strlen(r->txt); + if (tlen) { + n->txt = strdup(r->txt); + if (!n->txt) { + free((char *)n->host); + free((char *)n->instance); + free(n); + return; + } + } else { + n->txt = NULL; + } + + memcpy((uint8_t *)n->addrv6.addr, r->addrv6, sizeof(ip6_addr_t)); + + mdns_result_t * o = server->search.results; + server->search.results = n; + n->next = o; +} + +/** + * @brief finds service from given service type + * @param server the server + * @param service service type to match + * @param proto proto to match + * + * @return the service item if found or NULL on error + */ +static mdns_srv_item_t * _mdns_get_service_item(mdns_server_t * server, const char * service, const char * proto) +{ + mdns_srv_item_t * s = server->services; + while(s) { + if (!strcmp(s->service->service, service) && !strcmp(s->service->proto, proto)) { + return s; + } + s = s->next; + } + return NULL; +} + +/** + * @brief creates/allocates new service + * @param service service type + * @param proto service proto + * @param port service port + * + * @return pointer to the service or NULL on error + */ +static mdns_service_t * _mdns_create_service(const char * service, const char * proto, uint16_t port) +{ + mdns_service_t * s = (mdns_service_t *)malloc(sizeof(mdns_service_t)); + if (!s) { + return NULL; + } + + s->priority = 0; + s->weight = 0; + s->txt_num_items = 0; + s->instance = NULL; + s->txt = NULL; + s->port = port; + + s->service = strdup(service); + if (!s->service) { + free(s); + return NULL; + } + + s->proto = strdup(proto); + if (!s->proto) { + free((char *)s->service); + free(s); + return NULL; + } + + return s; +} + +/** + * @brief free service memory + * + * @param service the service + */ +static void _mdns_free_service(mdns_service_t * service) +{ + if (!service) { + return; + } + free((char *)service->instance); + free((char *)service->service); + free((char *)service->proto); + if (service->txt_num_items) { + uint8_t i; + for(i=0; itxt_num_items; i++) { + free((char *)service->txt[i]); + } + } + free(service->txt); + free(service); +} + +/** + * @brief read uint16_t from a packet + * @param packet the packet + * @param index index in the packet where the value starts + * + * @return the value + */ +static inline uint16_t _mdns_read_u16(const uint8_t * packet, uint16_t index) +{ + return (uint16_t)(packet[index]) << 8 | packet[index+1]; +} + +/** + * @brief main packet parser + * + * @param server the server + * @param data byte array holding the packet data + * @param len length of the byte array + */ +static void _mdns_parse_packet(mdns_server_t * server, const uint8_t * data, size_t len) +{ + static mdns_name_t n; + static mdns_result_temp_t a; + + const uint8_t * content = data + MDNS_HEAD_LEN; + mdns_name_t * name = &n; + memset(name, 0, sizeof(mdns_name_t)); + + uint16_t questions = _mdns_read_u16(data, MDNS_HEAD_QUESTIONS_OFFSET); + uint16_t answers = _mdns_read_u16(data, MDNS_HEAD_ANSWERS_OFFSET); + uint16_t additional = _mdns_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET); + + if (questions) { + uint8_t qs = questions; + mdns_answer_item_t * answers = NULL; + + while(qs--) { + content = _mdns_parse_fqdn(data, content, name); + if (!content) { + break;//error + } + + uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET); + content = content + 4; + + if (!name->service[0] || !name->proto[0]) { + if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA || type == MDNS_TYPE_ANY) {//send A + AAAA + if (name->host[0] && server->hostname && server->hostname[0] && !strcmp(name->host, server->hostname)) { + answers = _mdns_add_answer(answers, NULL, MDNS_ANSWER_A); + } + } + continue; + } + + if (name->sub) { + continue; + } + + mdns_srv_item_t * si = _mdns_get_service_item(server, name->service, name->proto); + if (!si) { + //service not found + continue; + } + + if (type == MDNS_TYPE_PTR) { + answers = _mdns_add_answer(answers, si->service, MDNS_ANSWER_ALL); + } else if (type == MDNS_TYPE_TXT) { + //match instance/host + const char * host = (si->service->instance)?si->service->instance + :(server->instance)?server->instance + :server->hostname; + if (!host || !host[0] || !name->host[0] || strcmp(name->host, host)) { + continue; + } + answers = _mdns_add_answer(answers, si->service, MDNS_ANSWER_TXT); + } else if (type == MDNS_TYPE_SRV) { + //match instance/host + const char * host = (si->service->instance)?si->service->instance + :(server->instance)?server->instance + :server->hostname; + if (!host || !host[0] || !name->host[0] || strcmp(name->host, host)) { + continue; + } + answers = _mdns_add_answer(answers, si->service, MDNS_ANSWER_SRV | MDNS_ANSWER_A); + } else if (type == MDNS_TYPE_ANY) {//send all + //match host + if (!name->host[0] || !server->hostname || !server->hostname[0] || strcmp(name->host, server->hostname)) { + answers = _mdns_add_answer(answers, si->service, MDNS_ANSWER_ALL); + } + } + } + if (answers) { + _mdns_send_answers(server, answers); + } + } + + if (server->search.running && (answers || additional)) { + mdns_result_temp_t * answer = &a; + memset(answer, 0, sizeof(mdns_result_temp_t)); + + while(content < (data + len)) { + content = _mdns_parse_fqdn(data, content, name); + if (!content) { + break;//error + } + uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET); + uint16_t data_len = _mdns_read_u16(content, MDNS_LEN_OFFSET); + const uint8_t * data_ptr = content + MDNS_DATA_OFFSET; + + content = data_ptr + data_len; + + if (type == MDNS_TYPE_PTR) { + if (!_mdns_parse_fqdn(data, data_ptr, name)) { + continue;//error + } + if (server->search.host[0] || + (strcmp(name->service, server->search.service) != 0) || + (strcmp(name->proto, server->search.proto) != 0)) { + continue;//not searching for service or wrong service/proto + } + sprintf(answer->instance, "%s", name->host); + } else if (type == MDNS_TYPE_SRV) { + if (server->search.host[0] || + (strcmp(name->service, server->search.service) != 0) || + (strcmp(name->proto, server->search.proto) != 0)) { + continue;//not searching for service or wrong service/proto + } + if (answer->instance[0]) { + if (strcmp(answer->instance, name->host) != 0) { + continue;//instance name is not the same as the one in the PTR record + } + } else { + sprintf(answer->instance, "%s", name->host); + } + //parse record value + if (!_mdns_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name)) { + continue;//error + } + + answer->ptr = 1; + answer->priority = _mdns_read_u16(data_ptr, MDNS_SRV_PRIORITY_OFFSET); + answer->weight = _mdns_read_u16(data_ptr, MDNS_SRV_WEIGHT_OFFSET); + answer->port = _mdns_read_u16(data_ptr, MDNS_SRV_PORT_OFFSET); + if (answer->host[0]) { + if (strcmp(answer->host, name->host) != 0) { + answer->addr = 0; + sprintf(answer->host, "%s", name->host); + } + } else { + sprintf(answer->host, "%s", name->host); + } + } else if (type == MDNS_TYPE_TXT) { + uint16_t i=0,b=0, y; + while(i < data_len) { + uint8_t partLen = data_ptr[i++]; + for(y=0; ytxt[b++] = d; + } + if (itxt[b++] = '&'; + } + } + answer->txt[b] = 0; + } else if (type == MDNS_TYPE_AAAA) { + if (server->search.host[0]) { + if (strcmp(name->host, server->search.host) != 0) { + continue;//wrong host + } + } else if (!answer->ptr) { + sprintf(answer->host, "%s", name->host); + } else if (strcmp(answer->host, name->host) != 0) { + continue;//wrong host + } + memcpy(answer->addrv6, data_ptr, sizeof(ip6_addr_t)); + } else if (type == MDNS_TYPE_A) { + if (server->search.host[0]) { + if (strcmp(name->host, server->search.host) != 0) { + continue;//wrong host + } + } else if (!answer->ptr) { + sprintf(answer->host, "%s", name->host); + } else if (strcmp(answer->host, name->host) != 0) { + continue;//wrong host + } + if (server->search.running && answer->addr) { + _mdns_add_result(server, answer);//another IP for our host + } + IP4_ADDR(answer, data_ptr[0], data_ptr[1], data_ptr[2], data_ptr[3]); + } + } + if (server->search.running && (server->search.host[0] || answer->ptr) && answer->addr) { + _mdns_add_result(server, answer); + } + //end while + } +} + + + +/* + * Public Methods + * */ +esp_err_t mdns_init(tcpip_adapter_if_t tcpip_if, mdns_server_t ** mdns_server) +{ + esp_err_t err = ESP_OK; + + if (tcpip_if >= TCPIP_ADAPTER_IF_MAX) { + return ESP_ERR_INVALID_ARG; + } + + if (_mdns_server_get(tcpip_if)) { + return ESP_ERR_INVALID_STATE; + } + + uint8_t mode; + err = esp_wifi_get_mode((wifi_mode_t*)&mode); + if (err) { + return err; + } + + if ((tcpip_if == TCPIP_ADAPTER_IF_STA && !(mode & WIFI_MODE_STA)) + || (tcpip_if == TCPIP_ADAPTER_IF_AP && !(mode & WIFI_MODE_AP))) { + return ESP_ERR_INVALID_ARG; + } + + mdns_server_t * server = (mdns_server_t *)malloc(sizeof(mdns_server_t)); + if (!server) { + return ESP_ERR_NO_MEM; + } + + server->tcpip_if = tcpip_if; + server->hostname = NULL; + server->instance = NULL; + server->services = NULL; + server->search.host[0] = 0; + server->search.service[0] = 0; + server->search.proto[0] = 0; + server->search.running = false; + server->search.results = NULL; + server->pcb = NULL; + + server->lock = xSemaphoreCreateMutex(); + if (!server->lock) { + free(server); + return ESP_ERR_NO_MEM; + } + + server->search.lock = xSemaphoreCreateMutex(); + if (!server->search.lock) { + vSemaphoreDelete(server->lock); + free(server); + return ESP_ERR_NO_MEM; + } + + server->queue = xQueueCreate(MDNS_PACKET_QUEUE_LEN, sizeof(struct pbuf *)); + if (!server->queue) { + vSemaphoreDelete(server->lock); + vSemaphoreDelete(server->search.lock); + free(server); + return ESP_ERR_NO_MEM; + } + + if (_mdns_server_add(server)) { + //service start failed! + vSemaphoreDelete(server->lock); + vSemaphoreDelete(server->search.lock); + vQueueDelete(server->queue); + free(server); + return ESP_FAIL; + } + + const char * hostname = NULL; + tcpip_adapter_get_hostname(server->tcpip_if, &hostname); + mdns_set_hostname(server, hostname); + + *mdns_server = server; + + return ESP_OK; +} + +void mdns_free(mdns_server_t * server) +{ + if (!server) { + return; + } + _mdns_server_remove(server); + mdns_service_remove_all(server); + MDNS_MUTEX_LOCK(); + free((char*)server->hostname); + free((char*)server->instance); + if (server->queue) { + struct pbuf * c; + while(xQueueReceive(server->queue, &c, 0) == pdTRUE) { + pbuf_free(c); + } + vQueueDelete(server->queue); + } + if (server->search.running) { + mdns_query_end(server); + } + mdns_result_free(server); + vSemaphoreDelete(server->search.lock); + MDNS_MUTEX_UNLOCK(); + vSemaphoreDelete(server->lock); + free(server); +} + +esp_err_t mdns_set_hostname(mdns_server_t * server, const char * hostname) +{ + if (!server) { + return ESP_ERR_INVALID_ARG; + } + MDNS_MUTEX_LOCK(); + free((char*)server->hostname); + server->hostname = (char *)malloc(strlen(hostname)+1); + if (!server->hostname) { + MDNS_MUTEX_UNLOCK(); + return ESP_ERR_NO_MEM; + } + sprintf((char *)server->hostname, "%s", hostname); + MDNS_MUTEX_UNLOCK(); + return ERR_OK; +} + +esp_err_t mdns_set_instance(mdns_server_t * server, const char * instance) +{ + if (!server) { + return ESP_ERR_INVALID_ARG; + } + MDNS_MUTEX_LOCK(); + free((char*)server->instance); + server->instance = (char *)malloc(strlen(instance)+1); + if (!server->instance) { + MDNS_MUTEX_UNLOCK(); + return ESP_ERR_NO_MEM; + } + sprintf((char *)server->instance, "%s", instance); + MDNS_MUTEX_UNLOCK(); + return ERR_OK; +} + +/* + * MDNS SERVICES + * */ + +esp_err_t mdns_service_add(mdns_server_t * server, const char * service, const char * proto, uint16_t port) +{ + if (!server || !service || !proto || !port) { + //bad argument + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * item = _mdns_get_service_item(server, service, proto); + if (item) { + //we already have that service + return mdns_service_port_set(server, service, proto, port); + } + + mdns_service_t * s = _mdns_create_service(service, proto, port); + if (!s) { + return ESP_ERR_NO_MEM; + } + + item = (mdns_srv_item_t *)malloc(sizeof(mdns_srv_item_t)); + if (!item) { + return ESP_ERR_NO_MEM; + } + + item->service = s; + item->next = server->services; + server->services = item; + return ESP_OK; +} + +esp_err_t mdns_service_port_set(mdns_server_t * server, const char * service, const char * proto, uint16_t port) +{ + if (!server || !server->services || !service || !proto || !port) { + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * s = _mdns_get_service_item(server, service, proto); + if (!s) { + return ESP_ERR_NOT_FOUND; + } + MDNS_MUTEX_LOCK(); + s->service->port = port; + MDNS_MUTEX_UNLOCK(); + return ESP_OK; +} + +esp_err_t mdns_service_txt_set(mdns_server_t * server, const char * service, const char * proto, uint8_t num_items, const char ** txt) +{ + if (!server || !server->services || !service || !proto) { + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * s = _mdns_get_service_item(server, service, proto); + if (!s) { + return ESP_ERR_NOT_FOUND; + } + MDNS_MUTEX_LOCK(); + if (s->service->txt_num_items) { + uint8_t i; + for(i=0; iservice->txt_num_items; i++) { + free((char *)s->service->txt[i]); + } + } + free(s->service->txt); + if (num_items) { + s->service->txt = (const char **)malloc(sizeof(char *) * num_items); + if (!s->service->txt) { + s->service->txt_num_items = 0; + goto fail; + } + uint8_t i; + s->service->txt_num_items = num_items; + for(i=0; iservice->txt[i] = strdup(txt[i]); + if (!s->service->txt[i]) { + s->service->txt_num_items = i; + goto fail; + } + } + } + MDNS_MUTEX_UNLOCK(); + return ESP_OK; +fail: + MDNS_MUTEX_UNLOCK(); + return ESP_ERR_NO_MEM; +} + +esp_err_t mdns_service_instance_set(mdns_server_t * server, const char * service, const char * proto, const char * instance) +{ + if (!server || !server->services || !service || !proto) { + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * s = _mdns_get_service_item(server, service, proto); + if (!s) { + return ESP_ERR_NOT_FOUND; + } + MDNS_MUTEX_LOCK(); + free((char*)s->service->instance); + s->service->instance = strdup(instance); + if (!s->service->instance) { + MDNS_MUTEX_UNLOCK(); + return ESP_ERR_NO_MEM; + } + MDNS_MUTEX_UNLOCK(); + return ESP_OK; +} + +esp_err_t mdns_service_remove(mdns_server_t * server, const char * service, const char * proto) +{ + if (!server || !server->services || !service || !proto) { + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * s = _mdns_get_service_item(server, service, proto); + if (!s) { + return ESP_ERR_NOT_FOUND; + } + //first item + if (server->services == s) { + MDNS_MUTEX_LOCK(); + server->services = server->services->next; + MDNS_MUTEX_UNLOCK(); + _mdns_free_service(s->service); + free(s); + return ESP_OK; + } + //not first item + mdns_srv_item_t * a = server->services; + while(a->next && a->next != s) { + a = a->next; + } + //next item of the current item is our item + if (a->next == s) { + MDNS_MUTEX_LOCK(); + a->next = s->next; + MDNS_MUTEX_UNLOCK(); + _mdns_free_service(s->service); + free(s); + return ESP_OK; + } + //how did we end here? + return ESP_FAIL; +} + +esp_err_t mdns_service_remove_all(mdns_server_t * server) +{ + if (!server) { + return ESP_ERR_INVALID_ARG; + } + if (!server->services) { + return ESP_OK; + } + MDNS_MUTEX_LOCK(); + mdns_srv_item_t * a = server->services; + server->services = NULL; + while(a) { + mdns_srv_item_t * s = a; + a = a->next; + _mdns_free_service(s->service); + free(s); + } + MDNS_MUTEX_UNLOCK(); + return ESP_OK; +} + +/* + * MDNS QUERY + * */ + +uint32_t mdns_query(mdns_server_t * server, const char * service, const char * proto, uint32_t timeout) +{ + if (!server || !service) { + return 0; + } + MDNS_SEARCH_LOCK(); + uint16_t qtype = MDNS_TYPE_PTR; + mdns_result_free(server); + if (proto) { + server->search.host[0] = 0; + snprintf(server->search.service, MDNS_NAME_MAX_LEN, "%s", service); + snprintf(server->search.proto, MDNS_NAME_MAX_LEN, "%s", proto); + } else { + snprintf(server->search.host, MDNS_NAME_MAX_LEN, "%s", service); + server->search.service[0] = 0; + server->search.proto[0] = 0; + qtype = MDNS_TYPE_A; + } + + uint8_t hostname_len = strlen(server->search.host); + uint8_t service_type_len = strlen(server->search.service); + uint8_t protoname_len = strlen(server->search.proto); + + size_t len = 23; //head+type+class+(strlen(local)+1)+fqdn_end + if (hostname_len) { + len += hostname_len + 1; + } else if (service_type_len) { + len += service_type_len + protoname_len + 2; + } + + uint8_t * packet = (uint8_t *)malloc(len); + if (!packet) { + return 0; + } + memset(packet, 0, len); + _mdns_set_u16(packet, MDNS_HEAD_QUESTIONS_OFFSET, 1); + uint16_t index = MDNS_HEAD_LEN; + + if (hostname_len) { + _mdns_append_string(packet, &index, server->search.host); + } else if (service_type_len) { + _mdns_append_string(packet, &index, server->search.service); + _mdns_append_string(packet, &index, server->search.proto); + } + + _mdns_append_string(packet, &index, MDNS_DEFAULT_DOMAIN); + _mdns_append_u8(packet, &index, 0); //fqdn_end + + _mdns_append_u16(packet, &index, qtype); + _mdns_append_u16(packet, &index, MDNS_CLASS_IN); + + MDNS_MUTEX_LOCK(); + size_t written = _mdns_server_write(server, packet, index); + MDNS_MUTEX_UNLOCK(); + free(packet); + if (written != index) { + return 0; + } + + server->search.running = true; + if (timeout) { + uint32_t startAt = xTaskGetTickCount() * portTICK_PERIOD_MS; + while(server->search.running && ((xTaskGetTickCount() * portTICK_PERIOD_MS) - startAt) < timeout) { + vTaskDelay(1 / portTICK_PERIOD_MS); + } + server->search.running = false; + MDNS_SEARCH_UNLOCK(); + return mdns_result_get_count(server); + } + return 0; +} + +size_t mdns_query_end(mdns_server_t * server) +{ + if (!server || !server->search.running) { + return 0; + } + server->search.running = false; + MDNS_SEARCH_UNLOCK(); + return mdns_result_get_count(server); +} + +esp_err_t mdns_result_free(mdns_server_t * server) +{ + if (!server || server->search.running || !server->search.results) { + return ESP_ERR_INVALID_ARG; + } + while(server->search.results) { + const mdns_result_t * r = server->search.results; + server->search.results = (mdns_result_t *)r->next; + free((char *)r->host); + free((char *)r->instance); + free((char *)r->txt); + free((mdns_result_t *)r); + } + server->search.results = NULL; + return ESP_OK; +} + +size_t mdns_result_get_count(mdns_server_t * server) +{ + if (!server || !server->search.results) { + return 0; + } + size_t len = 0; + const mdns_result_t * r = server->search.results; + while(r) { + len++; + r = r->next; + } + return len; +} + +const mdns_result_t * mdns_result_get(mdns_server_t * server, size_t num) +{ + if (!server || !server->search.results) { + return NULL; + } + size_t len = 0; + const mdns_result_t * r = server->search.results; + while(r) { + if (len++ == num) { + return r; + } + r = r->next; + } + return NULL; +} diff --git a/components/newlib/include/stdatomic.h b/components/newlib/include/stdatomic.h index 09c0cf73e0..beba325b1a 100644 --- a/components/newlib/include/stdatomic.h +++ b/components/newlib/include/stdatomic.h @@ -32,6 +32,7 @@ #include #include +#include #if __has_extension(c_atomic) || __has_extension(cxx_atomic) #define __CLANG_ATOMICS diff --git a/components/newlib/include/sys/dirent.h b/components/newlib/include/sys/dirent.h deleted file mode 100644 index a3fb5c02c5..0000000000 --- a/components/newlib/include/sys/dirent.h +++ /dev/null @@ -1,13 +0,0 @@ -/* includes , which is this file. On a - system which supports , this file is overridden by - dirent.h in the libc/sys/.../sys directory. On a system which does - not support , we will get this file which uses #error to force - an error. */ - -#ifdef __cplusplus -extern "C" { -#endif -#error " not supported" -#ifdef __cplusplus -} -#endif diff --git a/components/newlib/lib/libc.a b/components/newlib/lib/libc.a index 4377fa252f..36a1cc6078 100644 Binary files a/components/newlib/lib/libc.a and b/components/newlib/lib/libc.a differ diff --git a/components/newlib/lib/libc_nano.a b/components/newlib/lib/libc_nano.a index 2a99a9c490..9afd379878 100644 Binary files a/components/newlib/lib/libc_nano.a and b/components/newlib/lib/libc_nano.a differ diff --git a/components/newlib/syscalls.c b/components/newlib/syscalls.c index 3b2fbf62ca..74182d07f2 100644 --- a/components/newlib/syscalls.c +++ b/components/newlib/syscalls.c @@ -22,15 +22,6 @@ #include "esp_attr.h" #include "freertos/FreeRTOS.h" -void IRAM_ATTR abort() -{ - do - { - __asm__ ("break 0,0"); - *((int*) 0) = 0; - } while(true); -} - void* IRAM_ATTR _malloc_r(struct _reent *r, size_t size) { return pvPortMalloc(size); diff --git a/components/newlib/test/test_newlib.c b/components/newlib/test/test_newlib.c index 1d86cd4a97..1d2a4bf09f 100644 --- a/components/newlib/test/test_newlib.c +++ b/components/newlib/test/test_newlib.c @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include "unity.h" #include "sdkconfig.h" @@ -86,6 +88,33 @@ TEST_CASE("test time functions", "[newlib]") } +TEST_CASE("test asctime", "[newlib]") +{ + char buf[64]; + struct tm tm = { 0 }; + tm.tm_year = 2016 - 1900; + tm.tm_mon = 0; + tm.tm_mday = 10; + tm.tm_hour = 16; + tm.tm_min = 30; + tm.tm_sec = 0; + time_t t = mktime(&tm); + const char* time_str = asctime(&tm); + strlcpy(buf, time_str, sizeof(buf)); + printf("Setting time: %s", time_str); + struct timeval now = { .tv_sec = t }; + settimeofday(&now, NULL); + + struct timeval tv; + gettimeofday(&tv, NULL); + time_t mtime = tv.tv_sec; + struct tm mtm; + localtime_r(&mtime, &mtm); + time_str = asctime(&mtm); + printf("Got time: %s", time_str); + TEST_ASSERT_EQUAL_STRING(buf, time_str); +} + static bool fn_in_rom(void *fn, char *name) { const int fnaddr = (int)fn; diff --git a/components/newlib/time.c b/components/newlib/time.c index 363e17b3eb..c9fa72eeee 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "esp_attr.h" #include "esp_intr_alloc.h" #include "soc/soc.h" @@ -58,9 +59,9 @@ static uint64_t get_rtc_time_us() // s_boot_time: time from Epoch to the first boot time #ifdef WITH_RTC -static RTC_DATA_ATTR struct timeval s_boot_time; +// when RTC is used to persist time, two RTC_STORE registers are used to store boot time #elif defined(WITH_FRC1) -static struct timeval s_boot_time; +static uint64_t s_boot_time; #endif #if defined(WITH_RTC) || defined(WITH_FRC1) @@ -88,6 +89,31 @@ static void IRAM_ATTR frc_timer_isr() #endif // WITH_FRC1 +static void set_boot_time(uint64_t time_us) +{ + _lock_acquire(&s_boot_time_lock); +#ifdef WITH_RTC + REG_WRITE(RTC_BOOT_TIME_LOW_REG, (uint32_t) (time_us & 0xffffffff)); + REG_WRITE(RTC_BOOT_TIME_HIGH_REG, (uint32_t) (time_us >> 32)); +#else + s_boot_time = time_us; +#endif + _lock_release(&s_boot_time_lock); +} + +static uint64_t get_boot_time() +{ + uint64_t result; + _lock_acquire(&s_boot_time_lock); +#ifdef WITH_RTC + result = ((uint64_t) REG_READ(RTC_BOOT_TIME_LOW_REG)) + (((uint64_t) REG_READ(RTC_BOOT_TIME_HIGH_REG)) << 32); +#else + result = s_boot_time; +#endif + _lock_release(&s_boot_time_lock); + return result; +} + void esp_setup_time_syscalls() { #if defined( WITH_FRC1 ) @@ -148,13 +174,10 @@ int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) { (void) tz; #if defined( WITH_FRC1 ) || defined( WITH_RTC ) - uint64_t microseconds = get_time_since_boot(); if (tv) { - _lock_acquire(&s_boot_time_lock); - microseconds += s_boot_time.tv_usec; - tv->tv_sec = s_boot_time.tv_sec + microseconds / 1000000; + uint64_t microseconds = get_boot_time() + get_time_since_boot(); + tv->tv_sec = microseconds / 1000000; tv->tv_usec = microseconds % 1000000; - _lock_release(&s_boot_time_lock); } return 0; #else @@ -168,14 +191,9 @@ int settimeofday(const struct timeval *tv, const struct timezone *tz) (void) tz; #if defined( WITH_FRC1 ) || defined( WITH_RTC ) if (tv) { - _lock_acquire(&s_boot_time_lock); uint64_t now = ((uint64_t) tv->tv_sec) * 1000000LL + tv->tv_usec; uint64_t since_boot = get_time_since_boot(); - uint64_t boot_time = now - since_boot; - - s_boot_time.tv_sec = boot_time / 1000000; - s_boot_time.tv_usec = boot_time % 1000000; - _lock_release(&s_boot_time_lock); + set_boot_time(now - since_boot); } return 0; #else diff --git a/components/nghttp/Makefile.projbuild b/components/nghttp/Makefile.projbuild index a93010ade9..5c6ce7fc9b 100644 --- a/components/nghttp/Makefile.projbuild +++ b/components/nghttp/Makefile.projbuild @@ -1,4 +1 @@ -# Anyone compiling mbedTLS code needs the name of the -# alternative config file - CFLAGS += -DHAVE_CONFIG_H diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h old mode 100644 new mode 100755 index 7f8eb88302..39d4bf737c --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -214,6 +214,14 @@ const SSL_METHOD* TLSv1_1_client_method(void); */ const SSL_METHOD* TLSv1_2_client_method(void); +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLS any version SSL context client method + */ +const SSL_METHOD* TLS_client_method(void); /** * @brief create the target SSL context server method @@ -260,6 +268,16 @@ const SSL_METHOD* TLSv1_server_method(void); */ const SSL_METHOD* SSLv3_server_method(void); +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLS any version SSL context server method + */ +const SSL_METHOD* TLS_server_method(void); + + /** * @brief set the SSL context ALPN select callback function * diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c old mode 100644 new mode 100755 index 522721ad7c..15015107f0 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -125,6 +125,9 @@ int ssl_pm_new(SSL *ssl) mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + } else { + mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); + mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); } mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); diff --git a/components/partition_table/Kconfig.projbuild b/components/partition_table/Kconfig.projbuild index 1f019a6e3f..36e435ba2f 100644 --- a/components/partition_table/Kconfig.projbuild +++ b/components/partition_table/Kconfig.projbuild @@ -45,8 +45,10 @@ config PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET config PARTITION_TABLE_FILENAME string - default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP - default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA + default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP && !ESP32_ENABLE_COREDUMP_TO_FLASH + default partitions_singleapp_coredump.csv if PARTITION_TABLE_SINGLE_APP && ESP32_ENABLE_COREDUMP_TO_FLASH + default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA && !ESP32_ENABLE_COREDUMP_TO_FLASH + default partitions_two_ota_coredump.csv if PARTITION_TABLE_TWO_OTA && ESP32_ENABLE_COREDUMP_TO_FLASH default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM config APP_OFFSET diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index a4412ad45b..0491204885 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -127,6 +127,7 @@ class PartitionDefinition(object): "ota" : 0x00, "phy" : 0x01, "nvs" : 0x02, + "coredump" : 0x03, "esphttpd" : 0x80, "fat" : 0x81, "spiffs" : 0x82, diff --git a/components/partition_table/partitions_singleapp_coredump.csv b/components/partition_table/partitions_singleapp_coredump.csv new file mode 100644 index 0000000000..a9f12c0fd3 --- /dev/null +++ b/components/partition_table/partitions_singleapp_coredump.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x6000 +phy_init, data, phy, 0xf000, 0x1000 +factory, app, factory, 0x10000, 1M +coredump, data, coredump,, 64K diff --git a/components/partition_table/partitions_two_ota_coredump.csv b/components/partition_table/partitions_two_ota_coredump.csv new file mode 100644 index 0000000000..64d70b0d8e --- /dev/null +++ b/components/partition_table/partitions_two_ota_coredump.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x4000 +otadata, data, ota, 0xd000, 0x2000 +phy_init, data, phy, 0xf000, 0x1000 +factory, 0, 0, 0x10000, 1M +coredump, data, coredump,, 64K +ota_0, 0, ota_0, , 1M +ota_1, 0, ota_1, , 1M diff --git a/components/sdmmc/component.mk b/components/sdmmc/component.mk new file mode 100755 index 0000000000..e69de29bb2 diff --git a/components/sdmmc/include/sdmmc_cmd.h b/components/sdmmc/include/sdmmc_cmd.h new file mode 100644 index 0000000000..58b6f082cc --- /dev/null +++ b/components/sdmmc/include/sdmmc_cmd.h @@ -0,0 +1,77 @@ +// Copyright 2015-2016 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 +#include "esp_err.h" +#include "driver/sdmmc_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 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); + +/** + * 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); + +/** + * Write given number of sectors to 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); + +#ifdef __cplusplus +} +#endif diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c new file mode 100644 index 0000000000..659ff5dd6c --- /dev/null +++ b/components/sdmmc/sdmmc_cmd.c @@ -0,0 +1,571 @@ +/* + * Copyright (c) 2006 Uwe Stuehler + * Adaptations to ESP-IDF Copyright (c) 2016 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 +#include "esp_log.h" +#include "esp_heap_alloc_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/sdmmc_defs.h" +#include "driver/sdmmc_types.h" +#include "sdmmc_cmd.h" + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +static const char* TAG = "sdmmc_cmd"; + +static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd); +static esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd); +static esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card); +static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr); +static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp); +static esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid); +static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid); +static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca); +static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd); +static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd); +static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd); +static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card); +static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr); +static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr); +static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width); +static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status); +static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status); +static uint32_t get_host_ocr(float voltage); + + +esp_err_t sdmmc_card_init(const sdmmc_host_t* config, + sdmmc_card_t* card) +{ + ESP_LOGD(TAG, "%s", __func__); + memset(card, 0, sizeof(*card)); + memcpy(&card->host, config, sizeof(*config)); + esp_err_t err = sdmmc_send_cmd_go_idle_state(card); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: go_idle_state (1) returned 0x%x", __func__, err); + return err; + } + ets_delay_us(10000); + uint32_t host_ocr = get_host_ocr(config->io_voltage); + err = sdmmc_send_cmd_send_if_cond(card, host_ocr); + if (err == ESP_OK) { + ESP_LOGD(TAG, "SDHC/SDXC card"); + host_ocr |= SD_OCR_SDHC_CAP; + } else if (err == ESP_ERR_TIMEOUT) { + ESP_LOGD(TAG, "CMD8 timeout; not an SDHC/SDXC card"); + } else { + ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err); + return err; + } + err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err); + return err; + } + host_ocr &= card->ocr; + ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr); + err = sddmc_send_cmd_all_send_cid(card, &card->cid); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err); + return err; + } + err = sdmmc_send_cmd_set_relative_addr(card, &card->rca); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err); + return err; + } + err = sdmmc_send_cmd_send_csd(card, &card->csd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err); + return err; + } + const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1; + if (!(card->ocr & SD_OCR_SDHC_CAP) && + card->csd.capacity > max_sdsc_capacity) { + ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.", + __func__, card->csd.capacity, max_sdsc_capacity); + card->csd.capacity = max_sdsc_capacity; + } + err = sdmmc_send_cmd_select_card(card); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err); + return err; + } + if ((card->ocr & SD_OCR_SDHC_CAP) == 0) { + err = sdmmc_send_cmd_set_blocklen(card, &card->csd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err); + return err; + } + } + err = sdmmc_send_cmd_send_scr(card, &card->scr); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err); + return err; + } + if ((config->flags & SDMMC_HOST_FLAG_4BIT) && + (card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) { + ESP_LOGD(TAG, "switching to 4-bit bus mode"); + err = sdmmc_send_cmd_set_bus_width(card, 4); + if (err != ESP_OK) { + ESP_LOGE(TAG, "set_bus_width failed"); + return err; + } + err = (*config->set_bus_width)(config->slot, 4); + if (err != ESP_OK) { + ESP_LOGE(TAG, "slot->set_bus_width failed"); + return err; + } + uint32_t status; + err = sdmmc_send_cmd_stop_transmission(card, &status); + if (err != ESP_OK) { + ESP_LOGE(TAG, "stop_transmission failed (0x%x)", err); + return err; + } + } + uint32_t status = 0; + while (!(status & MMC_R1_READY_FOR_DATA)) { + // TODO: add some timeout here + uint32_t count = 0; + err = sdmmc_send_cmd_send_status(card, &status); + if (err != ESP_OK) { + return err; + } + if (++count % 10 == 0) { + ESP_LOGV(TAG, "waiting for card to become ready (%d)", count); + } + } + if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED && + card->csd.tr_speed / 1000 >= SDMMC_FREQ_HIGHSPEED) { + ESP_LOGD(TAG, "switching to HS bus mode"); + err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED); + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode"); + return err; + } + } else if (config->max_freq_khz >= SDMMC_FREQ_DEFAULT && + card->csd.tr_speed / 1000 >= SDMMC_FREQ_DEFAULT) { + ESP_LOGD(TAG, "switching to DS bus mode"); + err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_DEFAULT); + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode"); + return err; + } + } + sdmmc_scr_t scr_tmp; + err = sdmmc_send_cmd_send_scr(card, &scr_tmp); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err); + return err; + } + if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) { + ESP_LOGE(TAG, "data check fail!"); + return ESP_ERR_INVALID_RESPONSE; + } + return ESP_OK; +} + +void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card) +{ + fprintf(stream, "Name: %s\n", card->cid.name); + fprintf(stream, "Type: %s\n", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC"); + fprintf(stream, "Speed: %s\n", (card->csd.tr_speed > 25000000)?"high speed":"default speed"); + fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024)); + fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n", + card->csd.csd_ver, + card->csd.sector_size, card->csd.capacity, card->csd.read_block_len); + fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width); +} + +static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd) +{ + int slot = card->host.slot; + ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d", + slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen); + esp_err_t err = (*card->host.do_transaction)(slot, cmd); + if (err != 0) { + ESP_LOGD(TAG, "sdmmc_req_run returned 0x%x", err); + return err; + } + int state = MMC_R1_CURRENT_STATE(cmd->response); + ESP_LOGV(TAG, "cmd response %08x %08x %08x %08x err=0x%x state=%d", + cmd->response[0], + cmd->response[1], + cmd->response[2], + cmd->response[3], + cmd->error, + state); + return cmd->error; +} + +static 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; + } + if (!(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); +} + + +static 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, + }; + return sdmmc_send_cmd(card, &cmd); +} + + +static 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) { + return ESP_ERR_INVALID_RESPONSE; + } + return ESP_OK; +} + +static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp) +{ + sdmmc_command_t cmd = { + .arg = ocr, + .flags = SCF_CMD_BCR | SCF_RSP_R3, + .opcode = SD_APP_OP_COND + }; + int nretries = 100; // arbitrary, BSD driver uses this value + for (; nretries != 0; --nretries) { + esp_err_t err = sdmmc_send_app_cmd(card, &cmd); + if (err != ESP_OK) { + return err; + } + if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) || + ocr == 0) { + break; + } + vTaskDelay(10 / portTICK_PERIOD_MS); + } + if (nretries == 0) { + return ESP_ERR_TIMEOUT; + } + if (ocrp) { + *ocrp = MMC_R3(cmd.response); + } + return ESP_OK; +} + +static 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; +} + +static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid) +{ + assert(out_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; + } + return sdmmc_decode_cid(cmd.response, out_cid); +} + + +static 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 + }; + + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + return err; + } + *out_rca = SD_R6_RCA(cmd.response); + return ESP_OK; +} + + +static 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); +} + +static 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; +} + +static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd) +{ + sdmmc_command_t cmd = { + .opcode = MMC_SEND_CSD, + .arg = MMC_ARG_RCA(card->rca), + .flags = SCF_CMD_AC | SCF_RSP_R2 + }; + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + return err; + } + return sdmmc_decode_csd(cmd.response, out_csd); +} + +static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card) +{ + sdmmc_command_t cmd = { + .opcode = MMC_SELECT_CARD, + .arg = MMC_ARG_RCA(card->rca), + .flags = SCF_CMD_AC | SCF_RSP_R1 + }; + return sdmmc_send_cmd(card, &cmd); +} + +static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr) +{ + sdmmc_response_t resp = {0xabababab, 0xabababab, 0x12345678, 0x09abcdef}; + resp[2] = __builtin_bswap32(raw_scr[0]); + resp[3] = __builtin_bswap32(raw_scr[1]); + int ver = SCR_STRUCTURE(resp); + if (ver != 0) { + return ESP_ERR_NOT_SUPPORTED; + } + out_scr->sd_spec = SCR_SD_SPEC(resp); + out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp); + return ESP_OK; +} + +static 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*) pvPortMallocCaps(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) { + buf[0] = (buf[0]); + buf[1] = (buf[1]); + err = sdmmc_decode_scr(buf, out_scr); + } + free(buf); + return err; +} + +static 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); +} + +static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status) +{ + sdmmc_command_t cmd = { + .opcode = MMC_STOP_TRANSMISSION, + .arg = 0, + .flags = SCF_RSP_R1B | SCF_CMD_AC + }; + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err == 0) { + *status = MMC_R1(cmd.response); + } + return err; +} + +static 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; +} + +static 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) { + *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) +{ + 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 + }; + 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; + while (!(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_read_sectors(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 (!(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; +} diff --git a/components/sdmmc/test/component.mk b/components/sdmmc/test/component.mk new file mode 100644 index 0000000000..ce464a212a --- /dev/null +++ b/components/sdmmc/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c new file mode 100644 index 0000000000..6c2154817f --- /dev/null +++ b/components/sdmmc/test/test_sd.c @@ -0,0 +1,109 @@ +// Copyright 2015-2016 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. + +#include +#include +#include +#include "unity.h" +#include "driver/gpio.h" +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +#include "esp_log.h" +#include "esp_heap_alloc_caps.h" +#include +#include + + +TEST_CASE("can probe SD", "[sd]") +{ + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + sdmmc_host_init(); + sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config); + sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t)); + TEST_ASSERT_NOT_NULL(card); + TEST_ESP_OK(sdmmc_card_init(&config, card)); + sdmmc_card_print_info(stdout, card); + sdmmc_host_deinit(); + free(card); +} + + +static void do_single_write_read_test(sdmmc_card_t* card, + size_t start_block, size_t block_count) +{ + size_t block_size = card->csd.sector_size; + size_t total_size = block_size * block_count; + printf(" %8d | %3d | %4.1f ", start_block, block_count, total_size / 1024.0f); + uint32_t* buffer = pvPortMallocCaps(total_size, MALLOC_CAP_DMA); + srand(start_block); + for (size_t i = 0; i < total_size / sizeof(buffer[0]); ++i) { + buffer[i] = rand(); + } + struct timeval t_start_wr; + gettimeofday(&t_start_wr, NULL); + TEST_ESP_OK(sdmmc_write_sectors(card, buffer, start_block, block_count)); + struct timeval t_stop_wr; + gettimeofday(&t_stop_wr, NULL); + float time_wr = 1e3f * (t_stop_wr.tv_sec - t_start_wr.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_wr.tv_usec); + memset(buffer, 0xbb, total_size); + struct timeval t_start_rd; + gettimeofday(&t_start_rd, NULL); + TEST_ESP_OK(sdmmc_read_sectors(card, buffer, start_block, block_count)); + struct timeval t_stop_rd; + gettimeofday(&t_stop_rd, NULL); + float time_rd = 1e3f * (t_stop_rd.tv_sec - t_start_rd.tv_sec) + 1e-3f * (t_stop_rd.tv_usec - t_start_rd.tv_usec); + + printf(" | %6.2f | %.2f | %.2fs | %.2f\n", + time_wr, total_size / (time_wr / 1000) / (1024 * 1024), + time_rd, total_size / (time_rd / 1000) / (1024 * 1024)); + srand(start_block); + for (size_t i = 0; i < total_size / sizeof(buffer[0]); ++i) { + TEST_ASSERT_EQUAL_HEX32(rand(), buffer[i]); + } + free(buffer); +} + +TEST_CASE("can write and read back blocks", "[sd]") +{ + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + config.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_host_init(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config); + sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t)); + TEST_ASSERT_NOT_NULL(card); + TEST_ESP_OK(sdmmc_card_init(&config, card)); + sdmmc_card_print_info(stdout, card); + printf(" sector | count | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n"); + do_single_write_read_test(card, 0, 1); + do_single_write_read_test(card, 0, 4); + do_single_write_read_test(card, 1, 16); + do_single_write_read_test(card, 16, 32); + do_single_write_read_test(card, 48, 64); + do_single_write_read_test(card, 128, 128); + do_single_write_read_test(card, card->csd.capacity - 64, 32); + do_single_write_read_test(card, card->csd.capacity - 64, 64); + do_single_write_read_test(card, card->csd.capacity - 8, 1); + do_single_write_read_test(card, card->csd.capacity/2, 1); + do_single_write_read_test(card, card->csd.capacity/2, 4); + do_single_write_read_test(card, card->csd.capacity/2, 8); + do_single_write_read_test(card, card->csd.capacity/2, 16); + do_single_write_read_test(card, card->csd.capacity/2, 32); + do_single_write_read_test(card, card->csd.capacity/2, 64); + do_single_write_read_test(card, card->csd.capacity/2, 128); + free(card); + sdmmc_host_deinit(); +} diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index f30db80cd8..983ff5d256 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -141,6 +141,29 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() esp_intr_noniram_enable(); } +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_no_os() +{ + const uint32_t cpuid = xPortGetCoreID(); + const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; + + // do not care about other CPU, it was halted upon entering panic handler + spi_flash_disable_cache(other_cpuid, &s_flash_op_cache_state[other_cpuid]); + // Kill interrupts that aren't located in IRAM + esp_intr_noniram_disable(); + // Disable cache on this CPU as well + spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); +} + +void IRAM_ATTR spi_flash_enable_interrupts_caches_no_os() +{ + const uint32_t cpuid = xPortGetCoreID(); + + // Re-enable cache on this CPU + spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]); + // Re-enable non-iram interrupts + esp_intr_noniram_enable(); +} + #else // CONFIG_FREERTOS_UNICORE void spi_flash_init_lock() @@ -172,6 +195,22 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() esp_intr_noniram_enable(); } +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_no_os() +{ + // Kill interrupts that aren't located in IRAM + esp_intr_noniram_disable(); + // Disable cache on this CPU as well + spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); +} + +void IRAM_ATTR spi_flash_enable_interrupts_caches_no_os() +{ + // Re-enable cache on this CPU + spi_flash_restore_cache(0, s_flash_op_cache_state[0]); + // Re-enable non-iram interrupts + esp_intr_noniram_enable(); +} + #endif // CONFIG_FREERTOS_UNICORE /** diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h index 899a31c651..598b8fba77 100644 --- a/components/spi_flash/cache_utils.h +++ b/components/spi_flash/cache_utils.h @@ -40,5 +40,12 @@ void spi_flash_disable_interrupts_caches_and_other_cpu(); // Enable cache, enable interrupts (to be added in future), resume scheduler void spi_flash_enable_interrupts_caches_and_other_cpu(); +// Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs. +// This function is implied to be called when other CPU is not running or running code from IRAM. +void spi_flash_disable_interrupts_caches_and_other_cpu_no_os(); + +// Enable cache, enable interrupts on current CPU. +// This function is implied to be called when other CPU is not running or running code from IRAM. +void spi_flash_enable_interrupts_caches_no_os(); #endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index 2165a784d1..15f75f3634 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -77,7 +77,7 @@ static void IRAM_ATTR spi_flash_mmap_init() } } -esp_err_t IRAM_ATTR spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, +esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory, const void** out_ptr, spi_flash_mmap_handle_t* out_handle) { esp_err_t ret; diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 993e68a572..fffe487bd1 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -60,6 +60,18 @@ static spi_flash_counters_t s_flash_stats; static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); +const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = { + .start = spi_flash_disable_interrupts_caches_and_other_cpu, + .end = spi_flash_enable_interrupts_caches_and_other_cpu +}; + +const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = { + .start = spi_flash_disable_interrupts_caches_and_other_cpu_no_os, + .end = spi_flash_enable_interrupts_caches_no_os +}; + +static const spi_flash_guard_funcs_t *s_flash_guard_ops; + void spi_flash_init() { spi_flash_init_lock(); @@ -68,6 +80,11 @@ void spi_flash_init() #endif } +void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs) +{ + s_flash_guard_ops = funcs; +} + size_t spi_flash_get_chip_size() { return g_rom_flashchip.chip_size; @@ -86,6 +103,18 @@ SpiFlashOpResult IRAM_ATTR spi_flash_unlock() return SPI_FLASH_RESULT_OK; } +static inline void spi_flash_guard_start() +{ + if (s_flash_guard_ops) + s_flash_guard_ops->start(); +} + +static inline void spi_flash_guard_end() +{ + if (s_flash_guard_ops) + s_flash_guard_ops->end(); +} + esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) { return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE); @@ -106,7 +135,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) size_t end = start + size / SPI_FLASH_SEC_SIZE; const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE; COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); + spi_flash_guard_start(); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { @@ -122,7 +151,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) } } } - spi_flash_enable_interrupts_caches_and_other_cpu(); + spi_flash_guard_end(); COUNTER_STOP(erase); return spi_flash_translate_rc(rc); } @@ -160,9 +189,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (left_size > 0) { uint32_t t = 0xffffffff; memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); - spi_flash_disable_interrupts_caches_and_other_cpu(); + spi_flash_guard_start(); rc = SPIWrite(left_off, &t, 4); - spi_flash_enable_interrupts_caches_and_other_cpu(); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -178,9 +207,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) bool in_dram = true; #endif if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) { - spi_flash_disable_interrupts_caches_and_other_cpu(); + spi_flash_guard_start(); rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size); - spi_flash_enable_interrupts_caches_and_other_cpu(); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -194,9 +223,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) uint32_t t[8]; uint32_t write_size = MIN(mid_size, sizeof(t)); memcpy(t, srcc + mid_off, write_size); - spi_flash_disable_interrupts_caches_and_other_cpu(); + spi_flash_guard_start(); rc = SPIWrite(dst + mid_off, t, write_size); - spi_flash_enable_interrupts_caches_and_other_cpu(); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -209,9 +238,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (right_size > 0) { uint32_t t = 0xffffffff; memcpy(&t, srcc + right_off, right_size); - spi_flash_disable_interrupts_caches_and_other_cpu(); + spi_flash_guard_start(); rc = SPIWrite(dst + right_off, &t, 4); - spi_flash_enable_interrupts_caches_and_other_cpu(); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -271,7 +300,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) SpiFlashOpResult rc = SPI_FLASH_RESULT_OK; COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); + spi_flash_guard_start(); /* To simplify boundary checks below, we handle small reads separately. */ if (size < 16) { uint32_t t[6]; /* Enough for 16 bytes + 4 on either side for padding. */ @@ -345,7 +374,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) memcpy(dstc + pad_right_off, t, pad_right_size); } out: - spi_flash_enable_interrupts_caches_and_other_cpu(); + spi_flash_guard_end(); COUNTER_STOP(read); return spi_flash_translate_rc(rc); } diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index b67891ae15..b149e10234 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -69,6 +69,7 @@ typedef enum { ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition + ESP_PARTITION_SUBTYPE_DATA_COREDUMP = 0x03, //!< COREDUMP partition ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80, //!< ESPHTTPD partition ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81, //!< FAT partition diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 91675088ae..bf897e8995 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -78,13 +78,13 @@ esp_err_t spi_flash_erase_range(size_t start_address, size_t size); * @note If source address is in DROM, this function will return * ESP_ERR_INVALID_ARG. * - * @param dest destination address in Flash. Must be a multiple of 4 bytes. - * @param src pointer to the source buffer. - * @param size length of data, in bytes. Must be a multiple of 4 bytes. + * @param dest_addr destination address in Flash. Must be a multiple of 4 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 4 bytes. * * @return esp_err_t */ -esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); +esp_err_t spi_flash_write(size_t dest_addr, const void *src, size_t size); /** @@ -97,24 +97,24 @@ esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); * @note If source address is in DROM, this function will return * ESP_ERR_INVALID_ARG. * - * @param dest destination address in Flash. Must be a multiple of 32 bytes. - * @param src pointer to the source buffer. - * @param size length of data, in bytes. Must be a multiple of 32 bytes. + * @param dest_addr destination address in Flash. Must be a multiple of 32 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 32 bytes. * * @return esp_err_t */ -esp_err_t spi_flash_write_encrypted(size_t dest, const void *src, size_t size); +esp_err_t spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size); /** * @brief Read data from Flash. * - * @param src source address of the data in Flash. - * @param dest pointer to the destination buffer - * @param size length of data + * @param src_addr source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data * * @return esp_err_t */ -esp_err_t spi_flash_read(size_t src, void *dest, size_t size); +esp_err_t spi_flash_read(size_t src_addr, void *dest, size_t size); /** * @brief Enumeration which specifies memory space requested in an mmap call @@ -149,7 +149,7 @@ typedef uint32_t spi_flash_mmap_handle_t; * * @return ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated */ -esp_err_t spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, +esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory, const void** out_ptr, spi_flash_mmap_handle_t* out_handle); /** @@ -173,6 +173,49 @@ void spi_flash_munmap(spi_flash_mmap_handle_t handle); */ void spi_flash_mmap_dump(); +/** + * @brief SPI flash critical section enter function. + */ +typedef void (*spi_flash_guard_start_func_t)(void); +/** + * @brief SPI flash critical section exit function. + */ +typedef void (*spi_flash_guard_end_func_t)(void); + +/** + * Structure holding SPI flash access critical section management functions + * + * @note Structure and corresponding guard functions should not reside in flash. + * For example structure can be placed in DRAM and functions in IRAM sections. + */ +typedef struct { + spi_flash_guard_start_func_t start; /**< critical section start func */ + spi_flash_guard_end_func_t end; /**< critical section end func */ +} spi_flash_guard_funcs_t; + +/** + * @brief Sets guard functions to access flash. + * + * @note Pointed structure and corresponding guard functions should not reside in flash. + * For example structure can be placed in DRAM and functions in IRAM sections. + * + * @param funcs pointer to structure holding flash access guard functions. + */ +void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs); + +/** + * @brief Default OS-aware flash access guard functions + */ +extern const spi_flash_guard_funcs_t g_flash_guard_default_ops; + +/** + * @brief Non-OS flash access guard functions + * + * @note This version of flash guard functions is to be used when no OS is present or from panic handler. + * It does not use any OS primitives and IPC and implies that only calling CPU is active. + */ +extern const spi_flash_guard_funcs_t g_flash_guard_no_os_ops; + #if CONFIG_SPI_FLASH_ENABLE_COUNTERS /** diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index 861f7ccb8d..4f3b49ed27 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -58,6 +58,17 @@ extern "C" { #define IPSTR "%d.%d.%d.%d" +#define IPV62STR(ipaddr) IP6_ADDR_BLOCK1(&(ipaddr)), \ + IP6_ADDR_BLOCK2(&(ipaddr)), \ + IP6_ADDR_BLOCK3(&(ipaddr)), \ + IP6_ADDR_BLOCK4(&(ipaddr)), \ + IP6_ADDR_BLOCK5(&(ipaddr)), \ + IP6_ADDR_BLOCK6(&(ipaddr)), \ + IP6_ADDR_BLOCK7(&(ipaddr)), \ + IP6_ADDR_BLOCK8(&(ipaddr)) + +#define IPV6STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" + typedef struct { ip4_addr_t ip; ip4_addr_t netmask; @@ -98,7 +109,7 @@ typedef struct { typedef enum { TCPIP_ADAPTER_IF_STA = 0, /**< ESP32 station interface */ TCPIP_ADAPTER_IF_AP, /**< ESP32 soft-AP interface */ - TCPIP_ADAPTER_IF_ETH, /**< ESP32 ethernet interface */ + TCPIP_ADAPTER_IF_ETH, /**< ESP32 ethernet interface */ TCPIP_ADAPTER_IF_MAX } tcpip_adapter_if_t; @@ -126,7 +137,7 @@ typedef enum{ } tcpip_adapter_option_id_t; /** - * @brief Initialize tcpip adpater + * @brief Initialize tcpip adapter * * This will initialize TCPIP stack inside. */ @@ -411,12 +422,12 @@ esp_interface_t tcpip_adapter_get_esp_if(void *dev); */ esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapter_sta_list_t *tcpip_sta_list); -#define TCPIP_HOSTNAME_MAX_SIZE 31 +#define TCPIP_HOSTNAME_MAX_SIZE 32 /** * @brief Set the hostname to the interface * * @param[in] tcpip_if: the interface which we will set the hostname - * @param[in] hostname: the host name for set the interfce + * @param[in] hostname: the host name for set the interface, the max length of hostname is 32 bytes * * @return ESP_OK:success * ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY:interface status error @@ -428,7 +439,7 @@ esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *ho * @brief Get the hostname from the interface * * @param[in] tcpip_if: the interface which we will get the hostname - * @param[in] hostname: the host name from the interfce + * @param[in] hostname: the host name from the interface * * @return ESP_OK:success * ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY:interface status error diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 726434b5fa..b2725e3447 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -529,7 +529,12 @@ static void tcpip_adapter_dhcpc_cb(struct netif *netif) } if ( !ip4_addr_cmp(ip_2_ip4(&netif->ip_addr), IP4_ADDR_ANY) ) { - tcpip_adapter_ip_info_t *ip_info = &esp_ip[TCPIP_ADAPTER_IF_STA]; + tcpip_adapter_ip_info_t *ip_info = NULL; + if( netif == esp_netif[TCPIP_ADAPTER_IF_STA] ) { + ip_info = &esp_ip[TCPIP_ADAPTER_IF_STA]; + } else if(netif == esp_netif[TCPIP_ADAPTER_IF_ETH] ) { + ip_info = &esp_ip[TCPIP_ADAPTER_IF_ETH]; + } //check whether IP is changed if ( !ip4_addr_cmp(ip_2_ip4(&netif->ip_addr), &ip_info->ip) || @@ -706,34 +711,35 @@ esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapt esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *hostname) { +#if LWIP_NETIF_HOSTNAME struct netif *p_netif; - static char hostinfo[TCPIP_HOSTNAME_MAX_SIZE + 1]; + static char hostinfo[TCPIP_HOSTNAME_MAX_SIZE + 1][TCPIP_ADAPTER_IF_MAX]; if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || hostname == NULL) { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; } - if (strlen(hostname) >= TCPIP_HOSTNAME_MAX_SIZE) { + if (strlen(hostname) > TCPIP_HOSTNAME_MAX_SIZE) { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; } p_netif = esp_netif[tcpip_if]; if (p_netif != NULL) { - if (netif_is_up(p_netif)) { - return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; - } else { - memset(hostinfo, 0, sizeof(hostinfo)); - memcpy(hostinfo, hostname, strlen(hostname)); - p_netif->hostname = hostinfo; - return ESP_OK; - } + memset(hostinfo[tcpip_if], 0, sizeof(hostinfo[tcpip_if])); + memcpy(hostinfo[tcpip_if], hostname, strlen(hostname)); + p_netif->hostname = hostinfo[tcpip_if]; + return ESP_OK; } else { - return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; } +#else + return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; +#endif } esp_err_t tcpip_adapter_get_hostname(tcpip_adapter_if_t tcpip_if, const char **hostname) { +#if LWIP_NETIF_HOSTNAME struct netif *p_netif = NULL; if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || hostname == NULL) { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; @@ -746,6 +752,9 @@ esp_err_t tcpip_adapter_get_hostname(tcpip_adapter_if_t tcpip_if, const char **h } else { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; } +#else + return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; +#endif } #endif diff --git a/components/vfs/README.rst b/components/vfs/README.rst index c58161c900..2ad7aa7ec2 100644 --- a/components/vfs/README.rst +++ b/components/vfs/README.rst @@ -23,11 +23,6 @@ To register an FS driver, application needs to define in instance of esp_vfs_t s .fstat = &myfs_fstat, .close = &myfs_close, .read = &myfs_read, - .lseek = NULL, - .stat = NULL, - .link = NULL, - .unlink = NULL, - .rename = NULL }; ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); @@ -125,4 +120,31 @@ When VFS component receives a call from newlib which has a file descriptor, this +-------------+ +Standard IO streams (stdin, stdout, stderr) +------------------------------------------- + +If "UART for console output" menuconfig option is not set to "None", then ``stdin``, ``stdout``, and ``stderr`` are configured to read from, and write to, a UART. It is possible to use UART0 or UART1 for standard IO. By default, UART0 is used, with 115200 baud rate, TX pin is GPIO1 and RX pin is GPIO3. These parameters can be changed in menuconfig. + +Writing to ``stdout`` or ``stderr`` will send characters to the UART transmit FIFO. Reading from ``stdin`` will retrieve characters from the UART receive FIFO. + +Note that while writing to ``stdout`` or ``stderr`` will block until all characters are put into the FIFO, reading from ``stdin`` is non-blocking. The function which reads from UART will get all the characters present in the FIFO (if any), and return. I.e. doing ``fscanf("%d\n", &var);`` may not have desired results. This is a temporary limitation which will be removed once ``fcntl`` is added to the VFS interface. + +Standard streams and FreeRTOS tasks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``FILE`` objects for ``stdin``, ``stdout``, and ``stderr`` are shared between all FreeRTOS tasks, but the pointers to these objects are are stored in per-task ``struct _reent``. The following code:: + + fprintf(stderr, "42\n"); + +actually is translated to to this (by the preprocessor): + + fprintf(__getreent()->_stderr, "42\n"); + +where the ``__getreent()`` function returns a per-task pointer to ``struct _reent`` (`source `_). This structure is allocated on the TCB of each task. When a task is initialized, ``_stdin``, ``_stdout`` and ``_stderr`` members of ``struct _reent`` are set to the values of ``_stdin``, ``_stdout`` and ``_stderr`` of ``_GLOBAL_REENT`` (i.e. the structure which is used before FreeRTOS is started). + +Such a design has the following consequences: + +- It is possible to set ``stdin``, ``stdout``, and ``stderr`` for any given task without affecting other tasks, e.g. by doing ``stdin = fopen("/dev/uart/1", "r")``. +- Closing default ``stdin``, ``stdout``, or ``stderr`` using ``fclose`` will close the ``FILE`` stream object — this will affect all other tasks. +- To change the default ``stdin``, ``stdout``, ``stderr`` streams for new tasks, modify ``_GLOBAL_REENT->_stdin`` (``_stdout``, ``_stderr``) before creating the task. diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 7dd273fb00..304750aabd 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -21,6 +21,8 @@ #include #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -106,6 +108,38 @@ typedef struct int (*rename_p)(void* ctx, const char *src, const char *dst); int (*rename)(const char *src, const char *dst); }; + union { + DIR* (*opendir_p)(void* ctx, const char* name); + DIR* (*opendir)(const char* name); + }; + union { + struct dirent* (*readdir_p)(void* ctx, DIR* pdir); + struct dirent* (*readdir)(DIR* pdir); + }; + union { + int (*readdir_r_p)(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent); + int (*readdir_r)(DIR* pdir, struct dirent* entry, struct dirent** out_dirent); + }; + union { + long (*telldir_p)(void* ctx, DIR* pdir); + long (*telldir)(DIR* pdir); + }; + union { + void (*seekdir_p)(void* ctx, DIR* pdir, long offset); + void (*seekdir)(DIR* pdir, long offset); + }; + union { + int (*closedir_p)(void* ctx, DIR* pdir); + int (*closedir)(DIR* pdir); + }; + union { + int (*mkdir_p)(void* ctx, const char* name, mode_t mode); + int (*mkdir)(const char* name, mode_t mode); + }; + union { + int (*rmdir_p)(void* ctx, const char* name); + int (*rmdir)(const char* name); + }; } esp_vfs_t; @@ -131,6 +165,15 @@ typedef struct esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx); +/** + * Unregister a virtual filesystem for given path prefix + * + * @param base_path file prefix previously used in esp_vfs_register call + * @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for given prefix + * hasn't been registered + */ +esp_err_t esp_vfs_unregister(const char* base_path); + /** * These functions are to be used in newlib syscall table. They will be called by * newlib when it needs to use any of the syscalls. diff --git a/components/vfs/include/sys/dirent.h b/components/vfs/include/sys/dirent.h new file mode 100644 index 0000000000..57b5be5eea --- /dev/null +++ b/components/vfs/include/sys/dirent.h @@ -0,0 +1,55 @@ +// Copyright 2015-2016 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 +#include + +/** + * This header file provides POSIX-compatible definitions of directory + * access functions and related data types. + * See http://pubs.opengroup.org/onlinepubs/7908799/xsh/dirent.h.html + * for reference. + */ + +/** + * @brief Opaque directory structure + */ +typedef struct { + uint16_t dd_vfs_idx; /*!< VFS index, not to be used by applications */ + uint16_t dd_rsv; /*!< field reserved for future extension */ + /* remaining fields are defined by VFS implementation */ +} DIR; + +/** + * @brief Directory entry structure + */ +struct dirent { + int d_ino; /*!< file number */ + uint8_t d_type; /*!< not defined in POSIX, but present in BSD and Linux */ +#define DT_UNKNOWN 0 +#define DT_REG 1 +#define DT_DIR 2 + char d_name[256]; /*!< zero-terminated file name */ +}; + +DIR* opendir(const char* name); +struct dirent* readdir(DIR* pdir); +long telldir(DIR* pdir); +void seekdir(DIR* pdir, long loc); +void rewinddir(DIR* pdir); +int closedir(DIR* pdir); +int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent); + diff --git a/components/vfs/test/component.mk b/components/vfs/test/component.mk new file mode 100644 index 0000000000..ce464a212a --- /dev/null +++ b/components/vfs/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/vfs/test/test_vfs_uart.c b/components/vfs/test/test_vfs_uart.c new file mode 100644 index 0000000000..892ca527c6 --- /dev/null +++ b/components/vfs/test/test_vfs_uart.c @@ -0,0 +1,124 @@ +// Copyright 2015-2016 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. + +#include +#include +#include +#include "unity.h" +#include "rom/uart.h" +#include "soc/uart_struct.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" + +static void fwrite_str_loopback(const char* str, size_t size) +{ + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + UART0.conf0.loopback = 1; + fwrite(str, 1, size, stdout); + fflush(stdout); + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + vTaskDelay(2 / portTICK_PERIOD_MS); + UART0.conf0.loopback = 0; +} + +static void flush_stdin_stdout() +{ + vTaskDelay(10 / portTICK_PERIOD_MS); + char *bitbucket = (char*) 0x3f000000; + while (fread(bitbucket, 1, 128, stdin) > 0) { + ; + } + fflush(stdout); + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); +} + +TEST_CASE("can read from stdin", "[vfs]") +{ + flush_stdin_stdout(); + + const size_t count = 12; + srand(count); + char* data = (char*) calloc(1, count * 8 + 1); + char* buf = (char*) calloc(1, count * 8 + 1); + char* p = data; + for (size_t i = 0; i < count; ++i) { + p += sprintf(p, "%08x", rand()); + } + p += sprintf(p, "\n"); + size_t len = p - data; + fwrite_str_loopback(data, len); + size_t cb = fread(buf, 1, len, stdin); + TEST_ASSERT_EQUAL(len, cb); + TEST_ASSERT_EQUAL_UINT8_ARRAY(data, buf, len); +} + + +#if CONFIG_NEWLIB_STDOUT_ADDCR + +TEST_CASE("CRs are removed from the stdin correctly", "[vfs]") +{ + flush_stdin_stdout(); + const char* send_str = "1234567890\n\r123\r\n4\n"; + /* with CONFIG_NEWLIB_STDOUT_ADDCR, the following will be sent on the wire. + * (last character of each part is marked with a hat) + * + * 1234567890\r\n\r123\r\r\n4\r\n + * ^ ^^ ^ + */ + char buf[128]; + char* dst = buf; + + fwrite_str_loopback(send_str, 11); // send up to the first \n + size_t rb = fread(dst, 1, 5, stdin); // read first 5 + TEST_ASSERT_EQUAL(5, rb); + dst += rb; + + rb = fread(dst, 1, 6, stdin); // ask for 6 + TEST_ASSERT_EQUAL(6, rb); // get 6 + + TEST_ASSERT_EQUAL_UINT8_ARRAY("1234567890\n", buf, 11); + dst += rb; + + rb = fread(dst, 1, 2, stdin); // any more characters? + TEST_ASSERT_EQUAL(0, rb); // nothing + + fwrite_str_loopback(send_str + 11, 1); // send the '\r' + vTaskDelay(10 / portTICK_PERIOD_MS); + + rb = fread(dst, 1, 2, stdin); // try to get somthing + TEST_ASSERT_EQUAL(0, rb); // still nothing (\r is buffered) + + fwrite_str_loopback(send_str + 12, 1); // Now send the '1' + vTaskDelay(10 / portTICK_PERIOD_MS); + rb = fread(dst, 1, 2, stdin); // try again + TEST_ASSERT_EQUAL(2, rb); // get two characters + TEST_ASSERT_EQUAL_UINT8_ARRAY("\r1", dst, 2); + dst += rb; + + fwrite_str_loopback(send_str + 13, 6); // Send the rest + vTaskDelay(10 / portTICK_PERIOD_MS); + + rb = fread(dst, 1, 4, stdin); // consume "23\r\n" + TEST_ASSERT_EQUAL(4, rb); + TEST_ASSERT_EQUAL_UINT8_ARRAY("23\r\n", dst, 4); + dst += rb; + + rb = fread(dst, 1, 4, stdin); // ask for more than the remainder + TEST_ASSERT_EQUAL(2, rb); + TEST_ASSERT_EQUAL_UINT8_ARRAY("4\n", dst, 2); +} + +#endif //CONFIG_NEWLIB_STDOUT_ADDCR + diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index b60c60a818..d80972a533 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -54,9 +54,6 @@ static size_t s_vfs_count = 0; esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx) { - if (s_vfs_count >= VFS_MAX_COUNT) { - return ESP_ERR_NO_MEM; - } size_t len = strlen(base_path); if (len < 2 || len > ESP_VFS_PATH_MAX) { return ESP_ERR_INVALID_ARG; @@ -68,16 +65,41 @@ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ct if (entry == NULL) { return ESP_ERR_NO_MEM; } + size_t index; + for (index = 0; index < s_vfs_count; ++index) { + if (s_vfs[index] == NULL) { + break; + } + } + if (index == s_vfs_count) { + if (s_vfs_count >= VFS_MAX_COUNT) { + free(entry); + return ESP_ERR_NO_MEM; + } + ++s_vfs_count; + } + s_vfs[index] = entry; strcpy(entry->path_prefix, base_path); // we have already verified argument length memcpy(&entry->vfs, vfs, sizeof(esp_vfs_t)); entry->path_prefix_len = len; entry->ctx = ctx; - entry->offset = s_vfs_count; - s_vfs[s_vfs_count] = entry; - ++s_vfs_count; + entry->offset = index; return ESP_OK; } +esp_err_t esp_vfs_unregister(const char* base_path) +{ + for (size_t i = 0; i < s_vfs_count; ++i) { + vfs_entry_t* vfs = s_vfs[i]; + if (memcmp(base_path, vfs->path_prefix, vfs->path_prefix_len) == 0) { + free(vfs); + s_vfs[i] = NULL; + return ESP_OK; + } + } + return ESP_ERR_INVALID_STATE; +} + static const vfs_entry_t* get_vfs_for_fd(int fd) { int index = ((fd & VFS_INDEX_MASK) >> VFS_INDEX_S); @@ -141,6 +163,28 @@ static const vfs_entry_t* get_vfs_for_path(const char* path) } +#define CHECK_AND_CALLV(r, pvfs, func, ...) \ + if (pvfs->vfs.func == NULL) { \ + __errno_r(r) = ENOSYS; \ + return; \ + } \ + if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ + (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \ + } else { \ + (*pvfs->vfs.func)(__VA_ARGS__);\ + } + +#define CHECK_AND_CALLP(ret, r, pvfs, func, ...) \ + if (pvfs->vfs.func == NULL) { \ + __errno_r(r) = ENOSYS; \ + return NULL; \ + } \ + if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ + ret = (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \ + } else { \ + ret = (*pvfs->vfs.func)(__VA_ARGS__);\ + } + int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode) { const vfs_entry_t* vfs = get_vfs_for_path(path); @@ -287,3 +331,116 @@ int esp_vfs_rename(struct _reent *r, const char *src, const char *dst) CHECK_AND_CALL(ret, r, vfs, rename, src_within_vfs, dst_within_vfs); return ret; } + +DIR* opendir(const char* name) +{ + const vfs_entry_t* vfs = get_vfs_for_path(name); + struct _reent* r = __getreent(); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return NULL; + } + const char* path_within_vfs = translate_path(vfs, name); + DIR* ret; + CHECK_AND_CALLP(ret, r, vfs, opendir, path_within_vfs); + if (ret != NULL) { + ret->dd_vfs_idx = vfs->offset << VFS_INDEX_S; + } + return ret; +} + +struct dirent* readdir(DIR* pdir) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return NULL; + } + struct dirent* ret; + CHECK_AND_CALLP(ret, r, vfs, readdir, pdir); + return ret; +} + +int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + errno = EBADF; + return -1; + } + int ret; + CHECK_AND_CALL(ret, r, vfs, readdir_r, pdir, entry, out_dirent); + return ret; +} + +long telldir(DIR* pdir) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + errno = EBADF; + return -1; + } + long ret; + CHECK_AND_CALL(ret, r, vfs, telldir, pdir); + return ret; +} + +void seekdir(DIR* pdir, long loc) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + errno = EBADF; + return; + } + CHECK_AND_CALLV(r, vfs, seekdir, pdir, loc); +} + +void rewinddir(DIR* pdir) +{ + seekdir(pdir, 0); +} + +int closedir(DIR* pdir) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + errno = EBADF; + return -1; + } + int ret; + CHECK_AND_CALL(ret, r, vfs, closedir, pdir); + return ret; +} + +int mkdir(const char* name, mode_t mode) +{ + const vfs_entry_t* vfs = get_vfs_for_path(name); + struct _reent* r = __getreent(); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, name); + int ret; + CHECK_AND_CALL(ret, r, vfs, mkdir, path_within_vfs, mode); + return ret; +} + +int rmdir(const char* name) +{ + const vfs_entry_t* vfs = get_vfs_for_path(name); + struct _reent* r = __getreent(); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, name); + int ret; + CHECK_AND_CALL(ret, r, vfs, rmdir, path_within_vfs); + return ret; +} diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index d9d755f9be..545a5474fa 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -70,6 +70,58 @@ static size_t IRAM_ATTR uart_write(int fd, const void * data, size_t size) return size; } +static ssize_t IRAM_ATTR uart_read(int fd, void* data, size_t size) +{ + assert(fd >=0 && fd < 3); + uint8_t *data_c = (uint8_t *) data; + uart_dev_t* uart = s_uarts[fd]; + size_t received = 0; + _lock_acquire_recursive(&s_uart_locks[fd]); + while (uart->status.rxfifo_cnt > 0 && received < size) { + uint8_t c = uart->fifo.rw_byte; +#if CONFIG_NEWLIB_STDOUT_ADDCR + /* Convert \r\n sequences to \n. + * If \r is received, it is put into 'buffered_char' until the next + * character is received. Then depending on the character, we either + * drop \r (if the next one is \n) or output \r and then proceed to output + * the new character. + */ + const int NONE = -1; + static int buffered_char = NONE; + if (buffered_char != NONE) { + if (buffered_char == '\r' && c == '\n') { + buffered_char = NONE; + } else { + data_c[received] = buffered_char; + buffered_char = NONE; + ++received; + if (received == size) { + /* We have placed the buffered character into the output buffer + * but there won't be enough space for the newly received one. + * Keep the new character in buffered_char until read is called + * again. + */ + buffered_char = c; + break; + } + } + } + if (c == '\r') { + buffered_char = c; + continue; + } +#endif //CONFIG_NEWLIB_STDOUT_ADDCR + data_c[received] = c; + ++received; + } + _lock_release_recursive(&s_uart_locks[fd]); + if (received > 0) { + return received; + } + errno = EWOULDBLOCK; + return -1; +} + static int IRAM_ATTR uart_fstat(int fd, struct stat * st) { assert(fd >=0 && fd < 3); @@ -92,7 +144,7 @@ void esp_vfs_dev_uart_register() .open = &uart_open, .fstat = &uart_fstat, .close = &uart_close, - .read = NULL, // TODO: implement reading from UART + .read = &uart_read, .lseek = NULL, .stat = NULL, .link = NULL, diff --git a/docs/Doxyfile b/docs/Doxyfile index 668ebaba02..aa6c87476e 100755 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -32,7 +32,12 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/esp32/include/esp_heap_alloc_caps.h \ ../components/freertos/include/freertos/heap_regions.h \ ../components/esp32/include/esp_smartconfig.h \ - ../components/esp32/include/esp_deep_sleep.h + ../components/esp32/include/esp_deep_sleep.h \ + ../components/sdmmc/include/sdmmc_cmd.h \ + ../components/fatfs/src/esp_vfs_fat.h \ + ../components/fatfs/src/diskio.h \ + ../components/esp32/include/esp_core_dump.h \ + ../components/mdns/include/mdns.h ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/api/bt.rst b/docs/api/bt.rst index 924e855c2f..2eae5dd4cf 100644 --- a/docs/api/bt.rst +++ b/docs/api/bt.rst @@ -4,7 +4,6 @@ Bluetooth .. toctree:: :caption: Bluetooth APIs - Bluetooth VHCI + Bluetooth Controller && VHCI Bluetooth Common - Bluetooth Classic Bluetooth LE diff --git a/docs/api/bt_common.rst b/docs/api/bt_common.rst index eaa6b8d31f..f2ce6bae9d 100644 --- a/docs/api/bt_common.rst +++ b/docs/api/bt_common.rst @@ -6,3 +6,4 @@ BT COMMON Bluetooth DEFINE Bluetooth MAIN + Bluetooth DEVICE diff --git a/docs/api/bt_le.rst b/docs/api/bt_le.rst index 6ae4869372..11d09809fe 100644 --- a/docs/api/bt_le.rst +++ b/docs/api/bt_le.rst @@ -8,3 +8,4 @@ BT COMMON BLE GATT DEFINE BLE GATT SERVER BLE GATT CLIENT + BLE BLUFI diff --git a/docs/api/vhci.rst b/docs/api/controller_vhci.rst similarity index 66% rename from docs/api/vhci.rst rename to docs/api/controller_vhci.rst index 0d5d8b5fff..a9c1a79289 100644 --- a/docs/api/vhci.rst +++ b/docs/api/controller_vhci.rst @@ -1,5 +1,5 @@ -VHCI -==== +Controller && VHCI +================== Overview -------- @@ -30,12 +30,24 @@ Header Files Type Definitions ^^^^^^^^^^^^^^^^ -.. doxygenstruct:: vhci_host_callback +.. doxygentypedef:: esp_vhci_host_callback_t + +Enumerations +^^^^^^^^^^^^ + + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: esp_vhci_host_callback + :members: + Functions ^^^^^^^^^ -.. doxygenfunction:: API_vhci_host_check_send_available -.. doxygenfunction:: API_vhci_host_register_callback -.. doxygenfunction:: API_vhci_host_send_packet -.. doxygenfunction:: bt_controller_init +.. doxygenfunction:: esp_bt_controller_init +.. doxygenfunction:: esp_vhci_host_check_send_available +.. doxygenfunction:: esp_vhci_host_send_packet +.. doxygenfunction:: esp_vhci_host_register_callback + diff --git a/docs/api/deep_sleep.rst b/docs/api/deep_sleep.rst index 3a458fb4a8..9e6642fd90 100644 --- a/docs/api/deep_sleep.rst +++ b/docs/api/deep_sleep.rst @@ -73,6 +73,8 @@ By default, ``esp_deep_sleep_start`` function will power down all RTC power doma Note: on the first revision of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep. +If some variables in the program are placed into RTC slow memory (for example, using ``RTC_DATA_ATTR`` attribute), RTC slow memory will be kept powered on by default. This can be overriden using ``esp_deep_sleep_pd_config`` function, if desired. + .. doxygenfunction:: esp_deep_sleep_pd_config .. doxygenenum:: esp_deep_sleep_pd_domain_t .. doxygenenum:: esp_deep_sleep_pd_option_t diff --git a/docs/api/esp_blufi.rst b/docs/api/esp_blufi.rst new file mode 100644 index 0000000000..13432dc154 --- /dev/null +++ b/docs/api/esp_blufi.rst @@ -0,0 +1,128 @@ +BLUFI API +========= + +Overview +-------- +BLUFI is a profile based GATT to config ESP32 WIFI to connect/disconnect AP or setup a softap and etc. +Use should concern these things: +1. The event sent from profile. Then you need to do something as the event indicate. +2. Security reference. You can write your own Security functions such as symmetrical encryption/decryption and checksum functions. Even you can define the "Key Exchange/Negotiation" procedure. + +Application Example +------------------- + +Check `/examples `_ folder of `espressif/esp-idf `_ repository, that contains the following example: + +`12_blufi `_ + +This is a BLUFI demo. This demo can set ESP32's wifi to softap/station/softap&station mode and config wifi connections. + + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `bt/bluedroid/api/include/esp_blufi_api.h `_ + +Macros +^^^^^^ + + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: esp_blufi_event_cb_t +.. doxygentypedef:: esp_blufi_negotiate_data_handler_t +.. doxygentypedef:: esp_blufi_encrypt_func_t +.. doxygentypedef:: esp_blufi_decrypt_func_t +.. doxygentypedef:: esp_blufi_checksum_func_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: esp_blufi_cb_event_t +.. doxygenenum:: esp_blufi_sta_conn_state_t +.. doxygenenum:: esp_blufi_init_state_t +.. doxygenenum:: esp_blufi_deinit_state_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: esp_blufi_extra_info_t + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_init_finish_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_deinit_finish_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_set_wifi_mode_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_connect_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_disconnect_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_sta_bssid_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_sta_ssid_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_sta_passwd_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_ssid_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_passwd_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_max_conn_num_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_auth_mode_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_channel_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_username_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_ca_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_client_cert_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_server_cert_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_client_pkey_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_server_pkey_evt_param + :members: + +.. doxygenstruct:: esp_blufi_callbacks_t + :members: + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: esp_blufi_register_callbacks +.. doxygenfunction:: esp_blufi_profile_init +.. doxygenfunction:: esp_blufi_profile_deinit +.. doxygenfunction:: esp_blufi_send_wifi_conn_report +.. doxygenfunction:: esp_blufi_get_version + diff --git a/docs/api/esp_bt_defs.rst b/docs/api/esp_bt_defs.rst index 9a2131f74c..801b13df78 100644 --- a/docs/api/esp_bt_defs.rst +++ b/docs/api/esp_bt_defs.rst @@ -40,7 +40,6 @@ Type Definitions ^^^^^^^^^^^^^^^^ .. doxygentypedef:: esp_bd_addr_t -.. doxygentypedef:: esp_profile_cb_t Enumerations ^^^^^^^^^^^^ diff --git a/docs/api/esp_bt_device.rst b/docs/api/esp_bt_device.rst new file mode 100644 index 0000000000..c344a5e633 --- /dev/null +++ b/docs/api/esp_bt_device.rst @@ -0,0 +1,48 @@ +BT DEVICE APIs +=============== + +Overview +-------- + +Bluetooth device reference APIs. + +`Instructions`_ + +Application Example +------------------- + +`Instructions`_ + +.. _Instructions: template.html + + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `bt/bluedroid/api/include/esp_bt_device.h `_ + + +Macros +^^^^^^ + + +Type Definitions +^^^^^^^^^^^^^^^^ + + +Enumerations +^^^^^^^^^^^^ + + +Structures +^^^^^^^^^^ + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: esp_bt_dev_get_address + diff --git a/docs/api/esp_bt_main.rst b/docs/api/esp_bt_main.rst index dc317c584f..48bb0c9cc0 100644 --- a/docs/api/esp_bt_main.rst +++ b/docs/api/esp_bt_main.rst @@ -34,6 +34,8 @@ Type Definitions Enumerations ^^^^^^^^^^^^ +.. doxygenenum:: esp_bluedroid_status_t + Structures ^^^^^^^^^^ @@ -42,8 +44,9 @@ Structures Functions ^^^^^^^^^ -.. doxygenfunction:: esp_enable_bluetooth -.. doxygenfunction:: esp_disable_bluetooth -.. doxygenfunction:: esp_init_bluetooth -.. doxygenfunction:: esp_deinit_bluetooth +.. doxygenfunction:: esp_bluedroid_get_status +.. doxygenfunction:: esp_bluedroid_enable +.. doxygenfunction:: esp_bluedroid_disable +.. doxygenfunction:: esp_bluedroid_init +.. doxygenfunction:: esp_bluedroid_deinit diff --git a/docs/api/esp_eth.rst b/docs/api/esp_eth.rst index d6d98e43d8..371aa5b233 100644 --- a/docs/api/esp_eth.rst +++ b/docs/api/esp_eth.rst @@ -21,14 +21,21 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ -.. doxygentypedef:: eth_phy_fun -.. doxygentypedef:: eth_tcpip_input_fun +.. doxygentypedef:: eth_phy_check_link_func +.. doxygentypedef:: eth_phy_check_init_func +.. doxygentypedef:: eth_phy_get_speed_mode_func +.. doxygentypedef:: eth_phy_get_duplex_mode_func +.. doxygentypedef:: eth_phy_func +.. doxygentypedef:: eth_tcpip_input_func .. doxygentypedef:: eth_gpio_config_func +.. doxygentypedef:: eth_phy_get_partner_pause_enable_func Enumerations ^^^^^^^^^^^^ .. doxygenenum:: eth_mode_t +.. doxygenenum:: eth_speed_mode_t +.. doxygenenum:: eth_duplex_mode_t .. doxygenenum:: eth_phy_base_t Structures @@ -48,4 +55,4 @@ Functions .. doxygenfunction:: esp_eth_get_mac .. doxygenfunction:: esp_eth_smi_write .. doxygenfunction:: esp_eth_smi_read - +.. doxygenfunction:: esp_eth_free_rx_buf diff --git a/docs/api/esp_gap_ble.rst b/docs/api/esp_gap_ble.rst index f1837d4025..856ed27302 100644 --- a/docs/api/esp_gap_ble.rst +++ b/docs/api/esp_gap_ble.rst @@ -33,11 +33,18 @@ Header Files Macros ^^^^^^ +.. doxygendefine:: ESP_BLE_ADV_FLAG_LIMIT_DISC +.. doxygendefine:: ESP_BLE_ADV_FLAG_GEN_DISC +.. doxygendefine:: ESP_BLE_ADV_FLAG_BREDR_NOT_SPT +.. doxygendefine:: ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT +.. doxygendefine:: ESP_BLE_ADV_FLAG_DMT_HOST_SPT +.. doxygendefine:: ESP_BLE_ADV_FLAG_NON_LIMIT_DISC .. doxygendefine:: ESP_BLE_ADV_DATA_LEN_MAX Type Definitions ^^^^^^^^^^^^^^^^ +.. doxygentypedef:: esp_gap_ble_cb_t Enumerations ^^^^^^^^^^^^ diff --git a/docs/api/esp_gatt_defs.rst b/docs/api/esp_gatt_defs.rst index b5a416d436..70e808a5d7 100644 --- a/docs/api/esp_gatt_defs.rst +++ b/docs/api/esp_gatt_defs.rst @@ -25,6 +25,29 @@ Header Files Macros ^^^^^^ + +.. doxygendefine:: ESP_GATT_UUID_IMMEDIATE_ALERT_SVC +.. doxygendefine:: ESP_GATT_UUID_LINK_LOSS_SVC +.. doxygendefine:: ESP_GATT_UUID_TX_POWER_SVC +.. doxygendefine:: ESP_GATT_UUID_CURRENT_TIME_SVC +.. doxygendefine:: ESP_GATT_UUID_REF_TIME_UPDATE_SVC +.. doxygendefine:: ESP_GATT_UUID_NEXT_DST_CHANGE_SVC +.. doxygendefine:: ESP_GATT_UUID_GLUCOSE_SVC +.. doxygendefine:: ESP_GATT_UUID_HEALTH_THERMOM_SVC +.. doxygendefine:: ESP_GATT_UUID_DEVICE_INFO_SVC +.. doxygendefine:: ESP_GATT_UUID_HEART_RATE_SVC +.. doxygendefine:: ESP_GATT_UUID_PHONE_ALERT_STATUS_SVC +.. doxygendefine:: ESP_GATT_UUID_BATTERY_SERVICE_SVC +.. doxygendefine:: ESP_GATT_UUID_BLOOD_PRESSURE_SVC +.. doxygendefine:: ESP_GATT_UUID_ALERT_NTF_SVC +.. doxygendefine:: ESP_GATT_UUID_HID_SVC +.. doxygendefine:: ESP_GATT_UUID_SCAN_PARAMETERS_SVC +.. doxygendefine:: ESP_GATT_UUID_RUNNING_SPEED_CADENCE_SVC +.. doxygendefine:: ESP_GATT_UUID_CYCLING_SPEED_CADENCE_SVC +.. doxygendefine:: ESP_GATT_UUID_CYCLING_POWER_SVC +.. doxygendefine:: ESP_GATT_UUID_LOCATION_AND_NAVIGATION_SVC +.. doxygendefine:: ESP_GATT_UUID_USER_DATA_SVC +.. doxygendefine:: ESP_GATT_UUID_WEIGHT_SCALE_SVC .. doxygendefine:: ESP_GATT_UUID_PRI_SERVICE .. doxygendefine:: ESP_GATT_UUID_SEC_SERVICE .. doxygendefine:: ESP_GATT_UUID_INCLUDE_SERVICE @@ -74,6 +97,9 @@ Macros .. doxygendefine:: ESP_GATT_UUID_HID_BT_KB_INPUT .. doxygendefine:: ESP_GATT_UUID_HID_BT_KB_OUTPUT .. doxygendefine:: ESP_GATT_UUID_HID_BT_MOUSE_INPUT +.. doxygendefine:: ESP_GATT_HEART_RATE_MEAS +.. doxygendefine:: ESP_GATT_BODY_SENSOR_LOCATION +.. doxygendefine:: ESP_GATT_HEART_RATE_CNTL_POINT .. doxygendefine:: ESP_GATT_UUID_BATTERY_LEVEL .. doxygendefine:: ESP_GATT_UUID_SC_CONTROL_POINT .. doxygendefine:: ESP_GATT_UUID_SENSOR_LOCATION @@ -84,7 +110,12 @@ Macros .. doxygendefine:: ESP_GATT_UUID_SCAN_INT_WINDOW .. doxygendefine:: ESP_GATT_UUID_SCAN_REFRESH .. doxygendefine:: ESP_GATT_ILLEGAL_UUID +.. doxygendefine:: ESP_GATT_ILLEGAL_HANDLE +.. doxygendefine:: ESP_GATT_ATTR_HANDLE_MAX .. doxygendefine:: ESP_GATT_MAX_ATTR_LEN +.. doxygendefine:: ESP_GATT_RSP_BY_APP +.. doxygendefine:: ESP_GATT_AUTO_RSP +.. doxygendefine:: ESP_GATT_IF_NONE Type Definitions ^^^^^^^^^^^^^^^^ @@ -105,6 +136,24 @@ Enumerations Structures ^^^^^^^^^^ +.. doxygenstruct:: esp_attr_desc_t + :members: + +.. doxygenstruct:: esp_attr_control_t + :members: + +.. doxygenstruct:: esp_gatts_attr_db_t + :members: + +.. doxygenstruct:: esp_attr_value_t + :members: + +.. doxygenstruct:: esp_gatts_incl_svc_desc_t + :members: + +.. doxygenstruct:: esp_gatts_incl128_svc_desc_t + :members: + .. doxygenstruct:: esp_gatt_value_t :members: diff --git a/docs/api/esp_gattc.rst b/docs/api/esp_gattc.rst index efd2623ad9..7ff1e9de7e 100644 --- a/docs/api/esp_gattc.rst +++ b/docs/api/esp_gattc.rst @@ -37,6 +37,7 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ +.. doxygentypedef:: esp_gattc_cb_t Enumerations ^^^^^^^^^^^^ diff --git a/docs/api/esp_gatts.rst b/docs/api/esp_gatts.rst index fdb0e055a9..4278e6b33e 100644 --- a/docs/api/esp_gatts.rst +++ b/docs/api/esp_gatts.rst @@ -37,6 +37,7 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ +.. doxygentypedef:: esp_gatts_cb_t Enumerations ^^^^^^^^^^^^ @@ -100,6 +101,12 @@ Structures .. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_rsp_evt_param :members: +.. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_add_attr_tab_evt_param + :members: + +.. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_set_attr_val_evt_param + :members: + Functions ^^^^^^^^^ @@ -108,6 +115,7 @@ Functions .. doxygenfunction:: esp_ble_gatts_app_register .. doxygenfunction:: esp_ble_gatts_app_unregister .. doxygenfunction:: esp_ble_gatts_create_service +.. doxygenfunction:: esp_ble_gatts_create_attr_tab .. doxygenfunction:: esp_ble_gatts_add_included_service .. doxygenfunction:: esp_ble_gatts_add_char .. doxygenfunction:: esp_ble_gatts_add_char_descr @@ -116,6 +124,8 @@ Functions .. doxygenfunction:: esp_ble_gatts_stop_service .. doxygenfunction:: esp_ble_gatts_send_indicate .. doxygenfunction:: esp_ble_gatts_send_response +.. doxygenfunction:: esp_ble_gatts_set_attr_value +.. doxygenfunction:: esp_ble_gatts_get_attr_value .. doxygenfunction:: esp_ble_gatts_open .. doxygenfunction:: esp_ble_gatts_close diff --git a/docs/api/fatfs.rst b/docs/api/fatfs.rst new file mode 100644 index 0000000000..d2efc87abf --- /dev/null +++ b/docs/api/fatfs.rst @@ -0,0 +1,64 @@ +FAT Filesystem Support +====================== + +ESP-IDF uses `FatFs `_ library to work with FAT filesystems. FatFs library resides in ``fatfs`` component. Although it can be used directly, many of its features can be accessed via VFS using C standard library and POSIX APIs. + +Additionally, FatFs has been modified to support run-time pluggable disk IO layer. This allows mapping of FatFs drives to physical disks at run-time. + +Using FatFs with VFS +-------------------- + +``esp_vfs_fat.h`` header file defines functions to connect FatFs with VFS. ``esp_vfs_fat_register`` function allocates a ``FATFS`` structure, and registers a given path prefix in VFS. Subsequent operations on files starting with this prefix are forwarded to FatFs APIs. ``esp_vfs_fat_unregister`` function deletes the registration with VFS, and frees the ``FATFS`` structure. + +Most applications will use the following flow when working with ``esp_vfs_fat_`` functions: + +1. Call ``esp_vfs_fat_register``, specifying path prefix where the filesystem has to be mounted (e.g. ``"/sdcard"``), FatFs drive number, and a variable which will receive a pointer to ``FATFS`` structure. + +2. Call ``ff_diskio_register`` function to register disk IO driver for the drive number used in step 1. + +3. Call ``f_mount`` function (and optionally ``f_fdisk``, ``f_mkfs``) to mount the filesystem using the same drive number which was passed to ``esp_vfs_fat_register``. See FatFs documentation for more details. + +4. Call POSIX and C standard library functions to open, read, write, erase, copy files, etc. Use paths starting with the prefix passed to ``esp_vfs_register`` (such as ``"/sdcard/hello.txt"``). + +5. Optionally, call FatFs library functions directly. Use paths without a VFS prefix in this case (``"/hello.txt"``). + +6. Close all open files. + +7. Call ``f_mount`` function for the same drive number, with NULL ``FATFS*`` argument, to unmount the filesystem. + +8. Call ``ff_diskio_register`` with NULL ``ff_diskio_impl_t*`` argument and the same drive number. + +9. Call ``esp_vfs_fat_unregister`` to remove FatFs from VFS, and free the ``FATFS`` structure allocated on step 1. + +Convenience functions, ``esp_vfs_fat_sdmmc_mount`` and ``esp_vfs_fat_sdmmc_unmount``, which wrap these steps and also handle SD card initialization, are described in the next section. + +.. doxygenfunction:: esp_vfs_fat_register +.. doxygenfunction:: esp_vfs_fat_unregister + + +Using FatFs with VFS and SD cards +--------------------------------- + +``esp_vfs_fat.h`` header file also provides a convenience function to perform steps 1–3 and 7–9, and also handle SD card initialization: ``esp_vfs_fat_sdmmc_mount``. This function does only limited error handling. Developers are encouraged to look at its source code and incorporate more advanced versions into production applications. ``esp_vfs_fat_sdmmc_unmount`` function unmounts the filesystem and releases resources acquired by ``esp_vfs_fat_sdmmc_mount``. + +.. doxygenfunction:: esp_vfs_fat_sdmmc_mount + +.. doxygenstruct:: esp_vfs_fat_sdmmc_mount_config_t + :members: + +.. doxygenfunction:: esp_vfs_fat_sdmmc_unmount + +FatFS disk IO layer +------------------- + +FatFs has been extended with an API to register disk IO driver at runtime. + +Implementation of disk IO functions for SD/MMC cards is provided. It can be registered for the given FatFs drive number using ``ff_diskio_register_sdmmc`` function. + +.. doxygenfunction:: ff_diskio_register + +.. doxygenstruct:: ff_diskio_impl_t + :members: + +.. doxygenfunction:: ff_diskio_register_sdmmc + diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst index 0cd4eca365..f331b9d265 100644 --- a/docs/api/gpio.rst +++ b/docs/api/gpio.rst @@ -4,20 +4,17 @@ GPIO Overview -------- -`Instructions`_ +The ESP32 chip features 40 physical GPIO pads. Some GPIO pads cannot be used or do not have the corresponding pin on the chip package(refer to technical reference manual ). Each pad can be used as a general purpose I/O or can be connected to an internal peripheral signal. +Note that GPIO6-11 are usually used for SPI flash. GPIO34-39 can only be set as input mode. Application Example ------------------- -`Instructions`_ +GPIO output and input interrupt example: `examples/21_gpio `_. API Reference ------------- -`Instructions`_ - -.. _Instructions: template.html - Header Files ^^^^^^^^^^^^ @@ -110,7 +107,8 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ -.. doxygentypedef:: gpio_event_callback +.. doxygentypedef:: gpio_isr_t +.. doxygentypedef:: gpio_isr_handle_t Enumerations ^^^^^^^^^^^^ @@ -122,6 +120,13 @@ Enumerations .. doxygenenum:: gpio_pulldown_t .. doxygenenum:: gpio_pull_mode_t +Structures +^^^^^^^^^^ + +.. doxygenstruct:: gpio_config_t + :members: + + Functions ^^^^^^^^^ @@ -136,3 +141,12 @@ Functions .. doxygenfunction:: gpio_wakeup_enable .. doxygenfunction:: gpio_wakeup_disable .. doxygenfunction:: gpio_isr_register +.. doxygenfunction:: gpio_pullup_en +.. doxygenfunction:: gpio_pullup_dis +.. doxygenfunction:: gpio_pulldown_en +.. doxygenfunction:: gpio_pulldown_dis +.. doxygenfunction:: gpio_install_isr_service +.. doxygenfunction:: gpio_uninstall_isr_service +.. doxygenfunction:: gpio_isr_handler_add +.. doxygenfunction:: gpio_isr_handler_remove + diff --git a/docs/api/i2c.rst b/docs/api/i2c.rst new file mode 100644 index 0000000000..2f89681f1d --- /dev/null +++ b/docs/api/i2c.rst @@ -0,0 +1,82 @@ +I2C +=========== + +Overview +-------- + +ESP32 has two I2C controllers which can be set as master mode or slave mode. + +Application Example +------------------- + +I2C master and slave example: `examples/18_i2c `_. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/i2c.h `_ + +Macros +^^^^^^ + +.. doxygendefine:: I2C_APB_CLK_FREQ +.. doxygendefine:: I2C_FIFO_LEN + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: i2c_cmd_handle_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: i2c_mode_t +.. doxygenenum:: i2c_rw_t +.. doxygenenum:: i2c_trans_mode_t +.. doxygenenum:: i2c_opmode_t +.. doxygenenum:: i2c_port_t +.. doxygenenum:: i2c_addr_mode_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: i2c_config_t + :members: + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: i2c_driver_install +.. doxygenfunction:: i2c_driver_delete +.. doxygenfunction:: i2c_param_config +.. doxygenfunction:: i2c_reset_tx_fifo +.. doxygenfunction:: i2c_reset_rx_fifo +.. doxygenfunction:: i2c_isr_register +.. doxygenfunction:: i2c_isr_free +.. doxygenfunction:: i2c_set_pin +.. doxygenfunction:: i2c_master_start +.. doxygenfunction:: i2c_master_write_byte +.. doxygenfunction:: i2c_master_write +.. doxygenfunction:: i2c_master_read_byte +.. doxygenfunction:: i2c_master_read +.. doxygenfunction:: i2c_master_stop +.. doxygenfunction:: i2c_master_cmd_begin +.. doxygenfunction:: i2c_slave_write_buffer +.. doxygenfunction:: i2c_slave_read +.. doxygenfunction:: i2c_set_period +.. doxygenfunction:: i2c_get_period +.. doxygenfunction:: i2c_set_start_timing +.. doxygenfunction:: i2c_get_start_timing +.. doxygenfunction:: i2c_set_stop_timing +.. doxygenfunction:: i2c_get_stop_timing +.. doxygenfunction:: i2c_set_data_timing +.. doxygenfunction:: i2c_get_data_timing +.. doxygenfunction:: i2c_set_data_mode +.. doxygenfunction:: i2c_get_data_mode +.. doxygenfunction:: i2c_cmd_link_create +.. doxygenfunction:: i2c_cmd_link_delete + diff --git a/docs/api/ledc.rst b/docs/api/ledc.rst index 855f822163..12d7cd16d0 100644 --- a/docs/api/ledc.rst +++ b/docs/api/ledc.rst @@ -4,40 +4,35 @@ LED Control Overview -------- -`Instructions`_ +The LED control module is primarily designed to control the intensity of LEDs, although it can be used to generate PWM signals for other purposes as well. +It has 16 channels which can generate independent waveforms that can be used to drive e.g. RGB LED devices. For maximum flexibility, the high-speed as well +as the low-speed channels can be driven from one of four high-speed/low-speed timers. The PWM controller also has the ability to automatically increase or +decrease the duty cycle gradually, allowing for fades without any processor interference. Application Example ------------------- -`Instructions`_ +LEDC change duty cycle and fading control example: `examples/29_ledc `_. API Reference ------------- -`Instructions`_ - -.. _Instructions: template.html - Header Files ^^^^^^^^^^^^ * `driver/include/driver/ledc.h `_ -Data Structures -^^^^^^^^^^^^^^^ - -.. doxygenstruct:: ledc_channel_config_t - :members: - -.. doxygenstruct:: ledc_timer_config_t - :members: - Macros ^^^^^^ .. doxygendefine:: LEDC_APB_CLK_HZ .. doxygendefine:: LEDC_REF_CLK_HZ +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: ledc_isr_handle_t + Enumerations ^^^^^^^^^^^^ @@ -49,6 +44,16 @@ Enumerations .. doxygenenum:: ledc_channel_t .. doxygenenum:: ledc_timer_bit_t +Structures +^^^^^^^^^^ + +.. doxygenstruct:: ledc_channel_config_t + :members: + +.. doxygenstruct:: ledc_timer_config_t + :members: + + Functions ^^^^^^^^^ @@ -67,3 +72,9 @@ Functions .. doxygenfunction:: ledc_timer_pause .. doxygenfunction:: ledc_timer_resume .. doxygenfunction:: ledc_bind_channel_timer +.. doxygenfunction:: ledc_set_fade_with_step +.. doxygenfunction:: ledc_set_fade_with_time +.. doxygenfunction:: ledc_fade_func_install +.. doxygenfunction:: ledc_fade_func_uninstall +.. doxygenfunction:: ledc_fade_start + diff --git a/docs/api/mdns.rst b/docs/api/mdns.rst new file mode 100644 index 0000000000..95789444f5 --- /dev/null +++ b/docs/api/mdns.rst @@ -0,0 +1,215 @@ +mDNS Service +============ + +Overview +-------- + +mDNS is a multicast UDP service that is used to provide local network service and host discovery. + +mDNS is installed by default on most operating systems or is available as separate package. On ``Mac OS`` it is installed by default and is called ``Bonjour``. Apple releases an installer for ``Windows`` that can be found `on Apple's support page `_. On ``Linux``, mDNS is provided by `avahi `_ and is usually installed by default. + +mDNS Properties +^^^^^^^^^^^^^^^ + + * ``hostname``: the hostname that the device will respond to. If not set, the ``hostname`` will be read from the interface. Example: ``my-esp32`` will resolve to ``my-esp32.local`` + * ``default_instance``: friendly name for your device, like ``Jhon's ESP32 Thing``. If not set, ``hostname`` will be used. + +Example method to start mDNS for the STA interface and set ``hostname`` and ``default_instance``: + + :: + + mdns_server_t * mdns = NULL; + + void start_mdns_service() + { + //initialize mDNS service on STA interface + esp_err_t err = mdns_init(TCPIP_ADAPTER_IF_STA, &mdns); + if (err) { + printf("MDNS Init failed: %d\n", err); + return; + } + + //set hostname + mdns_set_hostname(mdns, "my-esp32"); + //set default instance + mdns_set_instance(mdns, "Jhon's ESP32 Thing"); + } + +mDNS Services +^^^^^^^^^^^^^ + +mDNS can advertise information about network services that your device offers. Each service is defined by a few properties. + + * ``service``: (required) service type, prepended with underscore. Some common types can be found `here `_. + * ``proto``: (required) protocol that the service runs on, prepended with underscore. Example: ``_tcp`` or ``_udp`` + * ``port``: (required) network port that the service runs on + * ``instance``: friendly name for your service, like ``Jhon's ESP32 Web Server``. If not defined, ``default_instance`` will be used. + * ``txt``: ``var=val`` array of strings, used to define properties for your service + +Example method to add a few services and different properties: + + :: + + void add_mdns_services() + { + //add our services + mdns_service_add(mdns, "_http", "_tcp", 80); + mdns_service_add(mdns, "_arduino", "_tcp", 3232); + mdns_service_add(mdns, "_myservice", "_udp", 1234); + + //NOTE: services must be added before their properties can be set + //use custom instance for the web server + mdns_service_instance_set(mdns, "_http", "_tcp", "Jhon's ESP32 Web Server"); + + const char * arduTxtData[4] = { + "board=esp32", + "tcp_check=no", + "ssh_upload=no", + "auth_upload=no" + }; + //set txt data for service (will free and replace current data) + mdns_service_txt_set(mdns, "_arduino", "_tcp", 4, arduTxtData); + + //change service port + mdns_service_port_set(mdns, "_myservice", "_udp", 4321); + } + +mDNS Query +^^^^^^^^^^ + +mDNS provides methods for browsing for services and resolving host's IP/IPv6 addresses. + Results are returned as a linked list of ``mdns_result_t`` objects. If the result is from host query, + it will contain only ``addr`` and ``addrv6`` if found. Service queries will populate all fields + in a result that were found. + +Example method to resolve host IPs: + + :: + + void resolve_mdns_host(const char * hostname) + { + printf("mDNS Host Lookup: %s.local\n", hostname); + //run search for 1000 ms + if (mdns_query(mdns, hostname, NULL, 1000)) { + //results were found + const mdns_result_t * results = mdns_result_get(mdns, 0); + //itterate through all results + size_t i = 1; + while(results) { + //print result information + printf(" %u: IP:" IPSTR ", IPv6:" IPV6STR "\n", i++ + IP2STR(&results->addr), IPV62STR(results->addrv6)); + //load next result. Will be NULL if this was the last one + results = results->next; + } + //free the results from memory + mdns_result_free(mdns); + } else { + //host was not found + printf(" Host Not Found\n"); + } + } + +Example method to resolve local services: + + :: + + void find_mdns_service(const char * service, const char * proto) + { + printf("mDNS Service Lookup: %s.%s\n", service, proto); + //run search for 1000 ms + if (mdns_query(mdns, service, proto, 1000)) { + //results were found + const mdns_result_t * results = mdns_result_get(mdns, 0); + //itterate through all results + size_t i = 1; + while(results) { + //print result information + printf(" %u: hostname:%s, instance:\"%s\", IP:" IPSTR ", IPv6:" IPV6STR ", port:%u, txt:%s\n", i++, + (results->host)?results->host:"NULL", (results->instance)?results->instance:"NULL", + IP2STR(&results->addr), IPV62STR(results->addrv6), + results->port, (results->txt)?results->txt:"\r"); + //load next result. Will be NULL if this was the last one + results = results->next; + } + //free the results from memory + mdns_result_free(mdns); + } else { + //service was not found + printf(" Service Not Found\n"); + } + } + +Example of using the methods above: + + :: + + void my_app_some_method(){ + //search for esp32-mdns.local + resolve_mdns_host("esp32-mdns"); + + //search for HTTP servers + find_mdns_service("_http", "_tcp"); + //or file servers + find_mdns_service("_smb", "_tcp"); //windows sharing + find_mdns_service("_afpovertcp", "_tcp"); //apple sharing + find_mdns_service("_nfs", "_tcp"); //NFS server + find_mdns_service("_ftp", "_tcp"); //FTP server + //or networked printer + find_mdns_service("_printer", "_tcp"); + find_mdns_service("_ipp", "_tcp"); + } + +Application Example +------------------- + +mDNS server/scanner example: `examples/30_mdns_example `_. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `components/mdns/include/mdns.h `_ + +Macros +^^^^^^ + + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: mdns_server_t +.. doxygentypedef:: mdns_result_t + +Enumerations +^^^^^^^^^^^^ + + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: mdns_result_s + :members: + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: mdns_init +.. doxygenfunction:: mdns_free +.. doxygenfunction:: mdns_set_hostname +.. doxygenfunction:: mdns_set_instance +.. doxygenfunction:: mdns_service_add +.. doxygenfunction:: mdns_service_remove +.. doxygenfunction:: mdns_service_instance_set +.. doxygenfunction:: mdns_service_txt_set +.. doxygenfunction:: mdns_service_port_set +.. doxygenfunction:: mdns_service_remove_all +.. doxygenfunction:: mdns_query +.. doxygenfunction:: mdns_query_end +.. doxygenfunction:: mdns_result_get_count +.. doxygenfunction:: mdns_result_get +.. doxygenfunction:: mdns_result_free + diff --git a/docs/api/sdmmc.rst b/docs/api/sdmmc.rst new file mode 100644 index 0000000000..e751852f95 --- /dev/null +++ b/docs/api/sdmmc.rst @@ -0,0 +1,95 @@ +SDMMC Host Peripheral +===================== + +Overview +-------- + +SDMMC peripheral supports SD and MMC memory cards and SDIO cards. SDMMC software builds on top of SDMMC driver and consists of the following parts: + +1. SDMMC host driver (``driver/sdmmc_host.h``) — this driver provides APIs to send commands to the slave device(s), send and receive data, and handling error conditions on the bus. + +2. SDMMC protocol layer (``sdmmc_cmd.h``) — this component handles specifics of SD protocol such as card initialization and data transfer commands. Despite the name, only SD (SDSC/SDHC/SDXC) cards are supported at the moment. Support for MCC/eMMC cards can be added in the future. + +Protocol layer works with the host via ``sdmmc_host_t`` structure. This structure contains pointers to various functions of the host. This design makes it possible to implement an SD host using SPI interface later. + +Application Example +------------------- + +An example which combines SDMMC driver with FATFS library is provided in ``examples/27_sd_card`` directory. This example initializes the card, writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information. + + +Protocol layer APIs +------------------- + +Protocol layer is given ``sdmmc_host_t`` structure which describes the SD/MMC host driver, lists its capabilites, and provides pointers to functions of the driver. Protocol layer stores card-specific information in ``sdmmc_card_t`` structure. When sending commands to the SD/MMC host driver, protocol layer uses ``sdmmc_command_t`` structure to describe the command, argument, expected return value, and data to transfer, if any. + +Normal usage of the protocol layer is as follows: + +1. Call the host driver functions to initialize the host (e.g. ``sdmmc_host_init``, ``sdmmc_host_init_slot``). +2. Call ``sdmmc_card_init`` to initialize the card, passing it host driver information (``host``) and a pointer to ``sdmmc_card_t`` structure which will be filled in (``card``). +3. To read and write sectors of the card, use ``sdmmc_read_sectors`` and ``sdmmc_write_sectors``, passing the pointer to card information structure (``card``). +4. When card is not used anymore, call the host driver function to disable SDMMC host peripheral and free resources allocated by the driver (e.g. ``sdmmc_host_deinit``). + +Most applications need to use the protocol layer only in one task; therefore the protocol layer doesn't implement any kind of locking on the ``sdmmc_card_t`` structure, or when accessing SDMMC host driver. Such locking has to be implemented in the higher layer, if necessary (e.g. in the filesystem driver). + +.. doxygenstruct:: sdmmc_host_t + :members: + +.. doxygendefine:: SDMMC_HOST_FLAG_1BIT +.. doxygendefine:: SDMMC_HOST_FLAG_4BIT +.. doxygendefine:: SDMMC_HOST_FLAG_8BIT +.. doxygendefine:: SDMMC_HOST_FLAG_SPI +.. doxygendefine:: SDMMC_FREQ_DEFAULT +.. doxygendefine:: SDMMC_FREQ_HIGHSPEED +.. doxygendefine:: SDMMC_FREQ_PROBING + +.. doxygenstruct:: sdmmc_command_t + :members: + +.. doxygenstruct:: sdmmc_card_t + :members: + +.. doxygenstruct:: sdmmc_csd_t + :members: + +.. doxygenstruct:: sdmmc_cid_t + :members: + +.. doxygenstruct:: sdmmc_scr_t + :members: + +.. doxygenfunction:: sdmmc_card_init +.. doxygenfunction:: sdmmc_write_sectors +.. doxygenfunction:: sdmmc_read_sectors + +SDMMC host driver APIs +---------------------- + +On the ESP32, SDMMC host peripheral has two slots: + +- Slot 0 (``SDMMC_HOST_SLOT_0``) is an 8-bit slot. It uses ``HS1_*`` signals in the PIN MUX. +- Slot 1 (``SDMMC_HOST_SLOT_1``) is a 4-bit slot. It uses ``HS2_*`` signals in the PIN MUX. + +Card Detect and Write Protect signals can be routed to arbitrary pins using GPIO matrix. To use these pins, set ``gpio_cd`` and ``gpio_wp`` members of ``sdmmc_slot_config_t`` structure when calling ``sdmmc_host_init_slot``. + +Of all the funtions listed below, only ``sdmmc_host_init``, ``sdmmc_host_init_slot``, and ``sdmmc_host_deinit`` will be used directly by most applications. Other functions, such as ``sdmmc_host_set_bus_width``, ``sdmmc_host_set_card_clk``, and ``sdmmc_host_do_transaction`` will be called by the SD/MMC protocol layer via function pointers in ``sdmmc_host_t`` structure. + +.. doxygenfunction:: sdmmc_host_init + +.. doxygendefine:: SDMMC_HOST_SLOT_0 +.. doxygendefine:: SDMMC_HOST_SLOT_1 +.. doxygendefine:: SDMMC_HOST_DEFAULT + +.. doxygenfunction:: sdmmc_host_init_slot + +.. doxygenstruct:: sdmmc_slot_config_t + :members: + +.. doxygendefine:: SDMMC_SLOT_NO_CD +.. doxygendefine:: SDMMC_SLOT_NO_WP +.. doxygendefine:: SDMMC_SLOT_CONFIG_DEFAULT + +.. doxygenfunction:: sdmmc_host_set_bus_width +.. doxygenfunction:: sdmmc_host_set_card_clk +.. doxygenfunction:: sdmmc_host_do_transaction +.. doxygenfunction:: sdmmc_host_deinit diff --git a/docs/api/sigmadelta.rst b/docs/api/sigmadelta.rst index acfdaaa27d..b03f049421 100644 --- a/docs/api/sigmadelta.rst +++ b/docs/api/sigmadelta.rst @@ -1,5 +1,5 @@ Sigma-delta Modulation -======== +====================== Overview -------- diff --git a/docs/api/spi_master.rst b/docs/api/spi_master.rst new file mode 100644 index 0000000000..5b57d85f42 --- /dev/null +++ b/docs/api/spi_master.rst @@ -0,0 +1,167 @@ +SPI Master driver +================= + +Overview +-------- + +The ESP32 has four SPI peripheral devices, called SPI0, SPI1, HSPI and VSPI. SPI0 is entirely dedicated to +the flash cache the ESP32 uses to map the SPI flash device it is connected to into memory. SPI1 is +connected to the same hardware lines as SPI0 and is used to write to the flash chip. HSPI and VSPI +are free to use. SPI1, HSPI and VSPI all have three chip select lines, allowing them to drive up to +three SPI devices each as a master. The SPI peripherals also can be used in slave mode, driven from +another SPI master. + +The spi_master driver +^^^^^^^^^^^^^^^^^^^^^ + +The spi_master driver allows easy communicating with SPI slave devices, even in a multithreaded environment. +It fully transparently handles DMA transfers to read and write data and automatically takes care of +multiplexing between different SPI slaves on the same master + +Terminology +^^^^^^^^^^^ + +The spi_master driver uses the following terms: + +* Host: The SPI peripheral inside the ESP32 initiating the SPI transmissions. One of SPI, HSPI or VSPI. (For + now, only HSPI or VSPI are actually supported in the driver; it will support all 3 peripherals + somewhere in the future.) +* Bus: The SPI bus, common to all SPI devices connected to one host. In general the bus consists of the + miso, mosi, sclk and optionally quadwp and quadhd signals. The SPI slaves are connected to these + signals in parallel. + + - miso - Also known as q, this is the input of the serial stream into the ESP32 + + - mosi - Also known as d, this is the output of the serial stream from the ESP32 + + - sclk - Clock signal. Each data bit is clocked out or in on the positive or negative edge of this signal + + - quadwp - Write Protect signal. Only used for 4-bit (qio/qout) transactions. + + - quadhd - Hold signal. Only used for 4-bit (qio/qout) transactions. + +* Device: A SPI slave. Each SPI slave has its own chip select (CS) line, which is made active when + a transmission to/from the SPI slave occurs. +* Transaction: One instance of CS going active, data transfer from and/or to a device happening, and + CS going inactive again. Transactions are atomic, as in they will never be interrupted by another + transaction. + + +SPI transactions +^^^^^^^^^^^^^^^^ + +A transaction on the SPI bus consists of five phases, any of which may be skipped: + +* The command phase. In this phase, a command (0-16 bit) is clocked out. +* The address phase. In this phase, an address (0-64 bit) is clocked out. +* The read phase. The slave sends data to the master. +* The write phase. The master sends data to the slave. + +In full duplex, the read and write phases are combined, causing the SPI host to read and +write data simultaneously. + +The command and address phase are optional in that not every SPI device will need to be sent a command +and/or address. Tis is reflected in the device configuration: when the ``command_bits`` or ``data_bits`` +fields are set to zero, no command or address phase is done. + +Something similar is true for the read and write phase: not every transaction needs both data to be written +as well as data to be read. When ``rx_buffer`` is NULL (and SPI_USE_RXDATA) is not set) the read phase +is skipped. When ``tx_buffer`` is NULL (and SPI_USE_TXDATA) is not set) the write phase is skipped. + +Using the spi_master driver +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Initialize a SPI bus by calling ``spi_bus_initialize``. Make sure to set the correct IO pins in + the ``bus_config`` struct. Take care to set signals that are not needed to -1. + +- Tell the driver about a SPI slave device conencted to the bus by calling spi_bus_add_device. + Make sure to configure any timing requirements the device has in the ``dev_config`` structure. + You should now have a handle for the device, to be used when sending it a transaction. + +- To interact with the device, fill one or more spi_transaction_t structure with any transaction + parameters you need. Either queue all transactions by calling ``spi_device_queue_trans``, later + quering the result using ``spi_device_get_trans_result``, or handle all requests synchroneously + by feeding them into ``spi_device_transmit``. + +- Optional: to unload the driver for a device, call ``spi_bus_remove_device`` with the device + handle as an argument + +- Optional: to remove the driver for a bus, make sure no more drivers are attached and call + ``spi_bus_free``. + + +Transaction data +^^^^^^^^^^^^^^^^ + +Normally, data to be transferred to or from a device will be read from or written to a chunk of memory +indicated by the ``rx_buffer`` and ``tx_buffer`` members of the transaction structure. The SPI driver +may decide to use DMA for transfers, so these buffers should be allocated in DMA-capable memory using +``pvPortMallocCaps(size, MALLOC_CAP_DMA)``. + +Sometimes, the amount of data is very small making it less than optimal allocating a separate buffer +for it. If the data to be transferred is 32 bits or less, it can be stored in the transaction struct +itself. For transmitted data, use the ``tx_data`` member for this and set the ``SPI_USE_TXDATA`` flag +on the transmission. For received data, use ``rx_data`` and set ``SPI_USE_RXDATA``. In both cases, do +not touch the ``tx_buffer`` or ``rx_buffer`` members, because they use the same memory locations +as ``tx_data`` and ``rx_data``. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `drivers/include/drivers/spi_master.h `_ + +Macros +^^^^^^ + +.. doxygendefine:: SPI_DEVICE_TXBIT_LSBFIRST +.. doxygendefine:: SPI_DEVICE_RXBIT_LSBFIRST +.. doxygendefine:: SPI_DEVICE_BIT_LSBFIRST +.. doxygendefine:: SPI_DEVICE_3WIRE +.. doxygendefine:: SPI_DEVICE_POSITIVE_CS +.. doxygendefine:: SPI_DEVICE_HALFDUPLEX +.. doxygendefine:: SPI_DEVICE_CLK_AS_CS + +.. doxygendefine:: SPI_TRANS_MODE_DIO +.. doxygendefine:: SPI_TRANS_MODE_QIO +.. doxygendefine:: SPI_TRANS_MODE_DIOQIO_ADDR +.. doxygendefine:: SPI_TRANS_USE_RXDATA +.. doxygendefine:: SPI_TRANS_USE_TXDATA + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: spi_device_handle_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: spi_host_device_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: spi_transaction_t + :members: + +.. doxygenstruct:: spi_bus_config_t + :members: + +.. doxygenstruct:: spi_device_interface_config_t + :members: + + + +Functions +--------- + +.. doxygenfunction:: spi_bus_initialize +.. doxygenfunction:: spi_bus_free +.. doxygenfunction:: spi_bus_add_device +.. doxygenfunction:: spi_bus_remove_device +.. doxygenfunction:: spi_device_queue_trans +.. doxygenfunction:: spi_device_get_trans_result +.. doxygenfunction:: spi_device_transmit + diff --git a/docs/api/template.rst b/docs/api/template.rst index 6feb7ba271..3cc8713ba4 100644 --- a/docs/api/template.rst +++ b/docs/api/template.rst @@ -40,10 +40,15 @@ Application Example *INSTRUCTIONS* - 1. Provide one or more practical examples to demonstrate functionality of this API. - 2. Break down the code into parts and describe functionality of each part. - 3. Provide screenshots if applicable. - + 1. Prepare one or more practical examples to demonstrate functionality of this API. + 2. Each example should follow pattern of projects located in ``esp-idf/examples/`` folder. + 3. Place example in this folder complete with ``README.md`` file. + 4. Provide overview of demonstrated functionality in ``README.md``. + 5. With good overview reader should be able to understand what example does without opening the source code. + 6. Depending on complexity of example, break down description of code into parts and provide overview of functionality of each part. + 7. Include flow diagram and screenshots of application output if applicable. + 8. Finally add in this section synopsis of each example together with link to respective folder in ``esp-idf/examples/``. + API Reference ------------- diff --git a/docs/api/vfs.rst b/docs/api/vfs.rst index 798aac5492..71550f111c 100644 --- a/docs/api/vfs.rst +++ b/docs/api/vfs.rst @@ -31,6 +31,7 @@ Functions ^^^^^^^^^ .. doxygenfunction:: esp_vfs_register +.. doxygenfunction:: esp_vfs_unregister .. doxygenfunction:: esp_vfs_write .. doxygenfunction:: esp_vfs_lseek .. doxygenfunction:: esp_vfs_read diff --git a/docs/build_system.rst b/docs/build_system.rst index 6687fa69ed..7a33a41256 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -166,6 +166,7 @@ The following variables are set at the project level, but exported for use in th - ``CONFIG_*``: Each value in the project configuration has a corresponding variable available in make. All names begin with ``CONFIG_``. - ``CC``, ``LD``, ``AR``, ``OBJCOPY``: Full paths to each tool from the gcc xtensa cross-toolchain. - ``HOSTCC``, ``HOSTLD``, ``HOSTAR``: Full names of each tool from the host native toolchain. +- ``IDF_VER``: Git version of ESP-IDF (produced by ``git describe``) If you modify any of these variables inside ``component.mk`` then this will not prevent other components from building but it may make your component hard to build and/or debug. @@ -267,6 +268,14 @@ To create a component KConfig file, it is easiest to start with one of the KConf For an example, see `Adding conditional configuration`. +Preprocessor Definitions +------------------------ + +ESP-IDF build systems adds the following C preprocessor definitions on the command line: + +- ``ESP_PLATFORM`` — Can be used to detect that build happens within ESP-IDF. +- ``IDF_VER`` — Defined to a git version string. E.g. ``v2.0`` for a tagged release or ``v1.0-275-g0efaa4f`` for an arbitrary commit. + Build Process Internals ----------------------- @@ -477,3 +486,12 @@ is set then the component can instruct the linker to link other binaries instead .. _esp-idf-template: https://github.com/espressif/esp-idf-template .. _GNU Make Manual: https://www.gnu.org/software/make/manual/make.html .. _[_f1]: Actually, some components in esp-idf are "pure configuration" components that don't have a component.mk file, only a Makefile.projbuild and/or Kconfig.projbuild file. However, these components are unusual and most components have a component.mk file. + + +Custom sdkconfig defaults +------------------------- + +For example projects or other projects where you don't want to specify a full sdkconfig configuration, but you do want to override some key values from the esp-idf defaults, it is possible to create a file ``sdkconfig.defaults`` in the project directory. This file will be used when running ``make defconfig``, or creating a new config from scratch. + +To override the name of this file, set the ``SDKCONFIG_DEFAULTS`` environment variable. + diff --git a/docs/core_dump.rst b/docs/core_dump.rst new file mode 100644 index 0000000000..a8e328996f --- /dev/null +++ b/docs/core_dump.rst @@ -0,0 +1,81 @@ +ESP32 Core Dump +=============== + +Overview +-------- + +ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analysis of software state at the moment of failure. +Upon the crash system enters panic state, prints some information and halts or reboots depending configuration. User can choose to generate core dump in order to analyse +the reason of failure on PC later on. Core dump contains snapshots of all tasks in the system at the moment of failure. Snapshots include tasks control blocks (TCB) and stacks. +So it is possible to find out what task, at what instruction (line of code) and what callstack of that task lead to the crash. +ESP-IDF provides special script `espcoredump.py` to help users to retrieve and analyse core dumps. This tool provides two commands for core dumps analysis: + +* info_corefile - prints crashed task's registers, callstack, list of available tasks in the system, memory regions and contents of memory stored in core dump (TCBs and stacks) +* dbg_corefile - creates core dump ELF file and runs GDB debug session with this file. User can examine memory, variables and tasks states manually. Note that since not all memory is saved in core dump only values of variables allocated on stack will be meaningfull + +Configuration +------------- + +Currently there are three options related to core dump generation which user can choose in configuration menu of the application (`make menuconfig`): + +* Disable core dump generation +* Save core dump to flash +* Print core dump to UART + +These options can be choosen in Components -> ESP32-specific config -> Core dump destination menu item. + +Save core dump to flash +----------------------- + +When this option is selected core dumps are saved to special partition on flash. When using default partition table files which are provided with ESP-IDF it automatically +allocates necessary space on flash, But if user wants to use its own layout file together with core dump feature it should define separate partition for core dump +as it is shown below:: + + # Name, Type, SubType, Offset, Size + # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild + nvs, data, nvs, 0x9000, 0x6000 + phy_init, data, phy, 0xf000, 0x1000 + factory, app, factory, 0x10000, 1M + coredump, data, coredump,, 64K + +There are no special requrements for partition name. It can be choosen according to the user application needs, but partition type should be 'data' and +sub-type should be 'coredump'. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes. +This overhead does not include size of TCB and stack for every task. So partirion size should be at least 20 + max tasks number x (12 + TCB size + max task stack size) bytes. + +The example of generic command to analyze core dump from flash is: `espcoredump.py -p info_corefile ` +or `espcoredump.py -p dbg_corefile ` + +Print core dump to UART +----------------------- + +When this option is selected base64-encoded core dumps are printed on UART upon system panic. In this case user should save core dump text body to some file manually and +then run the following command: `espcoredump.py -p info_corefile -t b64 -c ` +or `espcoredump.py -p dbg_corefile -t b64 -c ` + +Base64-encoded body of core dump will be between the following header and footer:: + + ================= CORE DUMP START ================= + + ================= CORE DUMP END =================== + +Running 'espcoredump.py' +------------------------------------ + +Generic command syntax: + +`espcoredump.py [options] command [args]` + +:Script Options: + * --chip,-c {auto,esp32}. Target chip type. Supported values are `auto` and `esp32`. + * --port,-p PORT. Serial port device. + * --baud,-b BAUD. Serial port baud rate used when flashing/reading. +:Commands: + * info_corefile. Retrieve core dump and print useful info. + * dbg_corefile. Retrieve core dump and start GDB session with it. +:Command Arguments: + * --gdb,-g GDB. Path to gdb to use for data retrieval. + * --core,-c CORE. Path to core dump file to use (if skipped core dump will be read from flash). + * --core-format,-t CORE_FORMAT. Specifies that file passed with "-c" is an ELF ("elf"), dumped raw binary ("raw") or base64-encoded ("b64") format. + * --off,-o OFF. Ofsset of coredump partition in flash (type "make partition_table" to see it). + * --save-core,-s SAVE_CORE. Save core to file. Othwerwise temporary core file will be deleted. Ignored with "-c". + * --print-mem,-m Print memory dump. Used only with "info_corefile". diff --git a/docs/index.rst b/docs/index.rst index ba25b49691..eafe102447 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,11 +1,7 @@ -ESP32 Programming Guide -======================= +ESP-IDF Programming Guide +========================= -.. caution:: - - Until ESP-IDF release 1.0, this documentation is a draft. It is incomplete and may have mistakes. Please mind your step! - -Documentation adressed to developers of applications for `ESP32 `_ by `Espressif `_ using `esp-idf `_. +This is the documentation for Espressif IoT Developement Framework (`esp-idf `_). ESP-IDF is the official development framework for the `ESP32 `_ chip. Contents: @@ -35,6 +31,7 @@ Contents: partition-tables build_system openocd + core_dump Flash encryption Secure Boot ULP coprocessor @@ -70,7 +67,7 @@ Contents: 6.4. UART 6.5. I2C - TBA 6.6. I2S - TBA - 6.7. SPI - TBA + 6.7. SPI - 6.8. CAN - TBA 6.9. SD Controller - TBA 6.10. Infrared - TBA @@ -107,18 +104,23 @@ Contents: LED Control Remote Control Timer + I2C Pulse Counter Sigma-delta Modulation + SD/MMC SPI Flash and Partition APIs + SPI Master API Logging Non-Volatile Storage Virtual Filesystem + FAT Filesystem Ethernet Interrupt Allocation Memory Allocation Deep Sleep deep-sleep-stub + mDNS Template .. toctree:: diff --git a/docs/linux-setup.rst b/docs/linux-setup.rst index cf5e78b63d..94d77f3605 100644 --- a/docs/linux-setup.rst +++ b/docs/linux-setup.rst @@ -10,13 +10,12 @@ Install some packages To compile with ESP-IDF you need to get the following packages: - Ubuntu and Debian:: - - sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial + + sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial - Arch:: - - sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial + sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial Step 1: Download binary toolchain for the ESP32 ================================================== @@ -25,17 +24,17 @@ ESP32 toolchain for Linux is available for download from Espressif website: - for 64-bit Linux:: - https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-59.tar.gz + https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz - for 32-bit Linux:: - https://dl.espressif.com/dl/xtensa-esp32-elf-linux32-1.22.0-59.tar.gz + https://dl.espressif.com/dl/xtensa-esp32-elf-linux32-1.22.0-61-gab8375a-5.2.0.tar.gz Download this file, then extract it to the location you prefer, for example:: mkdir -p ~/esp cd ~/esp - tar -xzf ~/Downloads/xtensa-esp32-elf-linux64-1.22.0-59.tar.gz + tar -xzf ~/Downloads/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz The toolchain will be extracted into ``~/esp/xtensa-esp32-elf/`` directory. @@ -49,15 +48,27 @@ Alternatively, you may create an alias for the above command. This way you can g Then when you need the toolchain you can type ``get_esp32`` on the command line and the toolchain will be added to your ``PATH``. +Arch Linux Users +---------------- + +To run the precompiled gdb (xtensa-esp32-elf-gdb) in Arch Linux requires ncurses 5, but Arch uses ncurses 6. Backwards compatibility libraries are available in AUR_ for native and lib32 configurations: +- https://aur.archlinux.org/packages/ncurses5-compat-libs/ +- https://aur.archlinux.org/packages/lib32-ncurses5-compat-libs/ + +(Alternatively, use crosstool-NG to compile a gdb that links against ncurses 6.) + + Alternative Step 1: Compile the toolchain from source using crosstool-NG ======================================================================== -Instead of downloading binary toolchain from Espressif website (Step 1 above) you may build the toolchain yourself. +Instead of downloading binary toolchain from Espressif website (Step 1 above) you may build the toolchain yourself. If you can't think of a reason why you need to build it yourself, then probably it's better to stick with the binary version. However, here are some of the reasons why you might want to compile it from source: - if you want to customize toolchain build configuration +- if you want to use a different GCC version (such as 4.8.5) + - if you want to hack gcc or newlib or libstdc++ - if you are curious and/or have time to spare @@ -70,7 +81,7 @@ In any case, here are the steps to compile the toolchain yourself. - Ubuntu:: - sudo apt-get install gawk gperf grep gettext ncurses python python-dev automake bison flex texinfo help2man libtool + sudo apt-get install gawk gperf grep gettext libncurses-dev python python-dev automake bison flex texinfo help2man libtool - Debian:: @@ -104,7 +115,7 @@ Open terminal, navigate to the directory you want to clone ESP-IDF and clone it git clone --recursive https://github.com/espressif/esp-idf.git -ESP-IDF will be downloaded into ``~/esp/esp-idf``. +ESP-IDF will be downloaded into ``~/esp/esp-idf``. Note the ``--recursive`` option! If you have already cloned ESP-IDF without this option, run another command to get all the submodules:: @@ -133,7 +144,7 @@ In terminal, go to the application directory which was obtained on the previous cd ~/esp/myapp -Type a command like this to set the path to ESP-IDF directory:: +Type a command like this to set the path to ESP-IDF directory:: export IDF_PATH=~/esp/esp-idf @@ -156,3 +167,4 @@ Further reading If you'd like to use the Eclipse IDE instead of running ``make``, check out the Eclipse setup guide in this directory. +.. _AUR: https://wiki.archlinux.org/index.php/Arch_User_Repository diff --git a/docs/macos-setup.rst b/docs/macos-setup.rst index 53c6fe54c8..eeed9e4e26 100644 --- a/docs/macos-setup.rst +++ b/docs/macos-setup.rst @@ -4,32 +4,26 @@ Set up of Toolchain for Mac OS Step 0: Prerequisites ===================== -Getting MacPorts or homebrew ----------------------------- +- install pip:: -Whether you compile the toolchain from source or download binary toolchain, there are some dependencies which need to be installed on macOS first. These dependencies are installed with one of the package managers: homebrew or MacPorts. If you have these already, you can skip the following instructions. + sudo easy_install pip -- Install XCode from Mac App Store -- Open Terminal.app and run ``xcode-select --install`` -- Run ``sudo xcodebuild -license`` and agree to XCode license -- Install MacPorts_ or homebrew_ - -.. _homebrew: http://brew.sh/ -.. _MacPorts: https://www.macports.org/install.php +- install pyserial + sudo pip install pyserial Step 1: Download binary toolchain for the ESP32 ================================================== ESP32 toolchain for macOS is available for download from Espressif website: -https://dl.espressif.com/dl/xtensa-esp32-elf-osx-1.22.0-59.tar.gz +https://dl.espressif.com/dl/xtensa-esp32-elf-osx-1.22.0-61-gab8375a-5.2.0.tar.gz Download this file, then extract it to the location you prefer, for example:: mkdir -p ~/esp cd ~/esp - tar -xzf ~/Downloads/xtensa-esp32-elf-osx-1.22.0-59.tar.gz + tar -xzf ~/Downloads/xtensa-esp32-elf-osx-1.22.0-61-gab8375a-5.2.0.tar.gz The toolchain will be extracted into ``~/esp/xtensa-esp32-elf/`` directory. @@ -46,12 +40,14 @@ Then when you need the toolchain you can type ``get_esp32`` on the command line Alternative Step 1: Compile the toolchain from source using crosstool-NG ======================================================================== -Instead of downloading binary toolchain from Espressif website (Step 1 above) you may build the toolchain yourself. +Instead of downloading binary toolchain from Espressif website (Step 1 above) you may build the toolchain yourself. If you can't think of a reason why you need to build it yourself, then probably it's better to stick with the binary version. However, here are some of the reasons why you might want to compile it from source: - if you want to customize toolchain build configuration +- if you want to use a different GCC version (such as 4.8.5) + - if you want to hack gcc or newlib or libstdc++ - if you are curious and/or have time to spare @@ -62,11 +58,18 @@ In any case, here are the steps to compile the toolchain yourself. - Install dependencies: + - Install either MacPorts_ or homebrew_ package manager. MacPorts needs a full XCode installation, while homebrew only needs XCode command line tools. + + .. _homebrew: http://brew.sh/ + .. _MacPorts: https://www.macports.org/install.php + - with MacPorts:: - sudo port install gsed gawk binutils gperf grep gettext ncurses + sudo port install gsed gawk binutils gperf grep gettext wget libtool autoconf automake - - with homebrew (*TODO: provide list of packages for homebrew*) + - with homebrew:: + + brew install gnu-sed gawk binutils gperftools gettext wget help2man libtool autoconf automake Create a case-sensitive filesystem image:: @@ -105,7 +108,7 @@ Open Terminal.app, navigate to the directory you want to clone ESP-IDF and clone git clone --recursive https://github.com/espressif/esp-idf.git -ESP-IDF will be downloaded into ``~/esp/esp-idf``. +ESP-IDF will be downloaded into ``~/esp/esp-idf``. Note the ``--recursive`` option! If you have already cloned ESP-IDF without this option, run another command to get all the submodules:: @@ -134,7 +137,7 @@ In Terminal.app, go to the application directory which was obtained on the previ cd ~/esp/myapp -Type a command like this to set the path to ESP-IDF directory:: +Type a command like this to set the path to ESP-IDF directory:: export IDF_PATH=~/esp/esp-idf @@ -158,4 +161,3 @@ Further reading =============== If you'd like to use the Eclipse IDE instead of running ``make``, check out the Eclipse setup guide in this directory. - diff --git a/docs/security/flash-encryption.rst b/docs/security/flash-encryption.rst index b73d8a9ad3..298a0a75bf 100644 --- a/docs/security/flash-encryption.rst +++ b/docs/security/flash-encryption.rst @@ -7,9 +7,6 @@ Flash Encryption is separate from the `Secure Boot` feature, and you can use fla **IMPORTANT: Enabling flash encryption limits your options for further updates of your ESP32. Make sure to read this document (including `Limitations of Flash Encryption` and understand the implications of enabling flash encryption.** -**IMPORTANT: Flash Encryption feature is currently enabled for development use only, with a key generated on the host. The recommended production configuration, where the flash encryption key is generated by the device on first boot, is currently disabled while final testing is done. This documentation refers to flash encryption keys being generated on first boot, however for now it is necessary to follow the additional steps shown under `Precalculated Flash Encryption Key`.** - - Background ---------- diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 7aaf9abf81..3c247d4a46 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -5,8 +5,6 @@ Secure Boot is a feature for ensuring only your code can run on the chip. Data l Secure Boot is separate from the `Flash Encryption` feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. -**IMPORTANT: Secure Boot feature is currently enabled for development use only, with a key generated on the host. The recommended production configuration, where the secure boot key is generated by the device on first boot, is currently disabled while final testing is done. This documentation refers to "One-Time Flashable" mode (where keys are generated on the device), but for now only the `Re-Flashable Software Bootloader` mode is available.** - Background ---------- diff --git a/docs/windows-setup.rst b/docs/windows-setup.rst index a425f5b3a0..00205fb78b 100644 --- a/docs/windows-setup.rst +++ b/docs/windows-setup.rst @@ -9,7 +9,7 @@ Windows doesn't have a built-in "make" environment, so as well as installing the The quick setup is to download the Windows all-in-one toolchain & MSYS zip file from dl.espressif.com: -https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20160816.zip +https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20170111.zip Unzip the zip file to C:\ and it will create an "msys32" directory with a pre-prepared environment. @@ -38,7 +38,7 @@ Another Alternative Step 1: Just download a toolchain If you already have an MSYS2 install or want to do things differently, you can download just the toolchain here: -https://dl.espressif.com/dl/xtensa-esp32-elf-win32-1.22.0-59.zip +https://dl.espressif.com/dl/xtensa-esp32-elf-win32-1.22.0-61-gab8375a-5.2.0.zip If you followed one of the above options for Step 1, you won't need this download. diff --git a/examples/01_hello_world/main/hello_world_main.c b/examples/01_hello_world/main/hello_world_main.c index 0e872522fa..c8b9f5f0c9 100644 --- a/examples/01_hello_world/main/hello_world_main.c +++ b/examples/01_hello_world/main/hello_world_main.c @@ -17,7 +17,7 @@ void hello_task(void *pvParameter) printf("Hello world!\n"); for (int i = 10; i >= 0; i--) { printf("Restarting in %d seconds...\n", i); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } printf("Restarting now.\n"); fflush(stdout); diff --git a/examples/02_blink/main/blink.c b/examples/02_blink/main/blink.c index 1e49e51b2f..f97572ac21 100644 --- a/examples/02_blink/main/blink.c +++ b/examples/02_blink/main/blink.c @@ -33,10 +33,10 @@ void blink_task(void *pvParameter) while(1) { /* Blink off (output low) */ gpio_set_level(BLINK_GPIO, 0); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); /* Blink on (output high) */ gpio_set_level(BLINK_GPIO, 1); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } } diff --git a/examples/02_blink/sdkconfig.defaults b/examples/02_blink/sdkconfig.defaults new file mode 100644 index 0000000000..8c618fb9b4 --- /dev/null +++ b/examples/02_blink/sdkconfig.defaults @@ -0,0 +1,2 @@ +# Disable WiFi stack by default +CONFIG_WIFI_ENABLED=n diff --git a/examples/03_http_request/main/http_request_main.c b/examples/03_http_request/main/http_request_main.c index 9fe1933373..3831ae65b9 100644 --- a/examples/03_http_request/main/http_request_main.c +++ b/examples/03_http_request/main/http_request_main.c @@ -115,7 +115,7 @@ static void http_get_task(void *pvParameters) if(err != 0 || res == NULL) { ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } @@ -129,7 +129,7 @@ static void http_get_task(void *pvParameters) if(s < 0) { ESP_LOGE(TAG, "... Failed to allocate socket."); freeaddrinfo(res); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } ESP_LOGI(TAG, "... allocated socket\r\n"); @@ -138,7 +138,7 @@ static void http_get_task(void *pvParameters) ESP_LOGE(TAG, "... socket connect failed errno=%d", errno); close(s); freeaddrinfo(res); - vTaskDelay(4000 / portTICK_RATE_MS); + vTaskDelay(4000 / portTICK_PERIOD_MS); continue; } @@ -148,7 +148,7 @@ static void http_get_task(void *pvParameters) if (write(s, REQUEST, strlen(REQUEST)) < 0) { ESP_LOGE(TAG, "... socket send failed"); close(s); - vTaskDelay(4000 / portTICK_RATE_MS); + vTaskDelay(4000 / portTICK_PERIOD_MS); continue; } ESP_LOGI(TAG, "... socket send success"); @@ -166,7 +166,7 @@ static void http_get_task(void *pvParameters) close(s); for(int countdown = 10; countdown >= 0; countdown--) { ESP_LOGI(TAG, "%d... ", countdown); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } ESP_LOGI(TAG, "Starting again!"); } diff --git a/examples/04_https_request/main/https_request_main.c b/examples/04_https_request/main/https_request_main.c index caf3f374a3..933d97ac83 100644 --- a/examples/04_https_request/main/https_request_main.c +++ b/examples/04_https_request/main/https_request_main.c @@ -362,7 +362,7 @@ static void https_get_task(void *pvParameters) for(int countdown = 10; countdown >= 0; countdown--) { ESP_LOGI(TAG, "%d...", countdown); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } ESP_LOGI(TAG, "Starting again!"); } diff --git a/examples/05_ble_adv/Makefile b/examples/05_ble_adv/Makefile index 3a913b817b..2319786d4a 100644 --- a/examples/05_ble_adv/Makefile +++ b/examples/05_ble_adv/Makefile @@ -6,11 +6,3 @@ PROJECT_NAME := ble_adv include $(IDF_PATH)/make/project.mk - -# Copy some defaults into the sdkconfig by default -# so BT stack is enabled -sdkconfig: sdkconfig.defaults - $(Q) cp $< $@ - -menuconfig: sdkconfig -defconfig: sdkconfig diff --git a/examples/05_ble_adv/main/app_bt.c b/examples/05_ble_adv/main/app_bt.c index 5bdbfd5c73..f0780c950c 100644 --- a/examples/05_ble_adv/main/app_bt.c +++ b/examples/05_ble_adv/main/app_bt.c @@ -73,7 +73,7 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len) return 0; } -static vhci_host_callback_t vhci_host_cb = { +static esp_vhci_host_callback_t vhci_host_cb = { controller_rcv_pkt_ready, host_rcv_pkt }; @@ -139,13 +139,13 @@ static uint16_t make_cmd_ble_set_adv_data(uint8_t *buf, uint8_t data_len, uint8_ static void hci_cmd_send_reset(void) { uint16_t sz = make_cmd_reset (hci_cmd_buf); - API_vhci_host_send_packet(hci_cmd_buf, sz); + esp_vhci_host_send_packet(hci_cmd_buf, sz); } static void hci_cmd_send_ble_adv_start(void) { uint16_t sz = make_cmd_ble_set_adv_enable (hci_cmd_buf, 1); - API_vhci_host_send_packet(hci_cmd_buf, sz); + esp_vhci_host_send_packet(hci_cmd_buf, sz); } static void hci_cmd_send_ble_set_adv_param(void) @@ -168,7 +168,7 @@ static void hci_cmd_send_ble_set_adv_param(void) peer_addr, adv_chn_map, adv_filter_policy); - API_vhci_host_send_packet(hci_cmd_buf, sz); + esp_vhci_host_send_packet(hci_cmd_buf, sz); } static void hci_cmd_send_ble_set_adv_data(void) @@ -185,7 +185,7 @@ static void hci_cmd_send_ble_set_adv_data(void) adv_data_len = 5 + name_len; uint16_t sz = make_cmd_ble_set_adv_data(hci_cmd_buf, adv_data_len, (uint8_t *)adv_data); - API_vhci_host_send_packet(hci_cmd_buf, sz); + esp_vhci_host_send_packet(hci_cmd_buf, sz); } /* @@ -195,11 +195,11 @@ void bleAdvtTask(void *pvParameters) { int cmd_cnt = 0; bool send_avail = false; - API_vhci_host_register_callback(&vhci_host_cb); + esp_vhci_host_register_callback(&vhci_host_cb); printf("BLE advt task start\n"); while (1) { vTaskDelay(1000 / portTICK_PERIOD_MS); - send_avail = API_vhci_host_check_send_available(); + send_avail = esp_vhci_host_check_send_available(); if (send_avail) { switch (cmd_cnt) { case 0: hci_cmd_send_reset(); ++cmd_cnt; break; @@ -214,7 +214,7 @@ void bleAdvtTask(void *pvParameters) void app_main() { - bt_controller_init(); + esp_bt_controller_init(); xTaskCreatePinnedToCore(&bleAdvtTask, "bleAdvtTask", 2048, NULL, 5, NULL, 0); } diff --git a/examples/05_ble_adv/sdkconfig.defaults b/examples/05_ble_adv/sdkconfig.defaults index e435f383c8..dcf4ad2c2d 100644 --- a/examples/05_ble_adv/sdkconfig.defaults +++ b/examples/05_ble_adv/sdkconfig.defaults @@ -5,10 +5,3 @@ # BT config # CONFIG_BT_ENABLED=y - -# -# ESP32-specific config -# -CONFIG_ESP32_ENABLE_STACK_BT=y -# CONFIG_ESP32_ENABLE_STACK_NONE is not set -CONFIG_MEMMAP_BT=y diff --git a/examples/07_nvs_rw_value/main/nvs_rw_value.c b/examples/07_nvs_rw_value/main/nvs_rw_value.c index dac2d4077e..1b3e06b859 100644 --- a/examples/07_nvs_rw_value/main/nvs_rw_value.c +++ b/examples/07_nvs_rw_value/main/nvs_rw_value.c @@ -72,7 +72,7 @@ void app_main() // Restart module for (int i = 10; i >= 0; i--) { printf("Restarting in %d seconds...\n", i); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } printf("Restarting now.\n"); fflush(stdout); diff --git a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c index 7c13c15ba7..0d4b7db4ee 100644 --- a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c +++ b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c @@ -164,7 +164,7 @@ void app_main() */ while (1) { if (gpio_get_level(GPIO_NUM_0) == 0) { - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); if(gpio_get_level(GPIO_NUM_0) == 0) { err = save_run_time(); if (err != ESP_OK) printf("Error (%d) saving run time blob to NVS!\n", err); @@ -173,6 +173,6 @@ void app_main() esp_restart(); } } - vTaskDelay(200 / portTICK_RATE_MS); + vTaskDelay(200 / portTICK_PERIOD_MS); } } diff --git a/examples/09_a2dp/components/bluedroid_demos/app_project/SampleAV.c b/examples/09_a2dp/components/bluedroid_demos/app_project/SampleAV.c index 49f25362b0..de2f1573b8 100644 --- a/examples/09_a2dp/components/bluedroid_demos/app_project/SampleAV.c +++ b/examples/09_a2dp/components/bluedroid_demos/app_project/SampleAV.c @@ -8,8 +8,8 @@ #include "freertos/timers.h" #include "esp_system.h" -#include "EspAudio.h" -#include "EspAudioCom.h" +// #include "EspAudio.h" +// #include "EspAudioCom.h" #include "bt_app_common.h" #include "esp_bt_stack_manager.h" @@ -87,28 +87,7 @@ static void key_press_task_handler(void *arg) } } -static void key_tmr_handler(TimerHandle_t xTimer) -{ - if (m_audio_state != ESP_A2D_AUDIO_STATE_STARTED) { - BT_APP_TRACE_EVENT("-----key_tmr_hdlr, return, audio state: %d\n", m_audio_state); - return; - } - - bt_app_evt_arg param; - memset(¶m, 0, sizeof(bt_app_evt_arg)); - if (m_key_state == 1) { - param.avrc_key.key_state = 1; - m_key_state = 0; - } else { - param.avrc_key.key_state = 0; - m_key_state = 1; - } - param.avrc_key.id = 0x41; // volume up - BT_APP_TRACE_EVENT("-----key_tmr_hdlr: %d, key_id %d---\n", m_key_state, param.avrc_key.id); - bt_app_transfer_context(bt_app_handle_evt, ESP_AVRC_KEY_STATE_TO, ¶m, sizeof(bt_app_evt_arg), NULL); -} - -static void bt_app_a2d_cb(uint32_t event, void *param) +static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) { switch (event) { case ESP_A2D_CONNECTION_STATE_EVT: @@ -150,7 +129,7 @@ static btrc_ctrl_callbacks_t btrc_ctrl_cb = { static void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len) { - EspAudioPlayerStreamWrite((uint8_t *)data, len, 10); + // EspAudioPlayerStreamWrite((uint8_t *)data, len, 10); } static void bt_app_handle_evt(uint16_t event, void *p_param) @@ -162,7 +141,7 @@ static void bt_app_handle_evt(uint16_t event, void *p_param) char *dev_name = "ESP_SPEAKER"; esp_bt_gap_set_device_name(dev_name); - esp_a2d_register_callback(bt_app_a2d_cb); + esp_a2d_register_callback(&bt_app_a2d_cb); esp_a2d_register_data_callback(bt_app_a2d_data_cb); esp_a2d_sink_init(); @@ -184,13 +163,6 @@ static void bt_app_handle_evt(uint16_t event, void *p_param) m_key_tmr == 0) { BT_APP_TRACE_EVENT("mm1\n"); xTaskCreate(key_press_task_handler, "keyT", 2048, NULL, 10, &xKeyTaskHandle); - #if 0 - int32_t key_tmr_id = 10; - m_key_tmr = xTimerCreate("appKeyTmr", 3000 / portTICK_PERIOD_MS, pdTRUE, (void *) key_tmr_id, key_tmr_handler); - if (xTimerStart(m_key_tmr, 10 / portTICK_PERIOD_MS) != pdTRUE) { - BT_APP_TRACE_EVENT(" timer start failed\n"); - } - #endif } break; } @@ -200,9 +172,9 @@ static void bt_app_handle_evt(uint16_t event, void *p_param) if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) { // temporarily hardcoded the PCM configuaration BT_APP_TRACE_EVENT("configure audio player\n"); - EspAudioPlayerStreamCfg(StreamSampleRate_44k, 2, StreamBitLen_16BIT); - EspAudio_SetupStream("stream.pcm", InputSrcType_Stream); - EspAudio_SetVolume(99); + // EspAudioPlayerStreamCfg(StreamSampleRate_44k, 2, StreamBitLen_16BIT); + // EspAudio_SetupStream("stream.pcm", InputSrcType_Stream); + // EspAudio_SetVolume(99); } break; } diff --git a/examples/09_a2dp/main/demo_main.c b/examples/09_a2dp/main/demo_main.c index 7d7bf118b2..9a4288a754 100755 --- a/examples/09_a2dp/main/demo_main.c +++ b/examples/09_a2dp/main/demo_main.c @@ -20,6 +20,6 @@ void app_main() // system_init(); // printf("Free memory: %d bytes\n", system_get_free_heap_size()); // EspAudio_Init(); - bt_controller_init(); + esp_bt_controller_init(); bt_app_task_start_up(); } diff --git a/examples/10_openssl_server/README.md b/examples/10_openssl_server/README.md old mode 100644 new mode 100755 index 333cb3d6a6..984a83c308 --- a/examples/10_openssl_server/README.md +++ b/examples/10_openssl_server/README.md @@ -5,7 +5,7 @@ The Example contains of OpenSSL server demo. First you should configure the project by "make menuconfig": Example Configuration -> 1. WIFI SSID: WIFI network to which your PC is also connected to. - 1. WIFI Password: WIFI password + 2. WIFI Password: WIFI password IF you want to test the OpenSSL server demo: 1. compile the code and load the firmware diff --git a/examples/10_openssl_server/main/Kconfig.projbuild b/examples/10_openssl_server/main/Kconfig.projbuild old mode 100644 new mode 100755 index 7a9cb97a0e..176d8fb334 --- a/examples/10_openssl_server/main/Kconfig.projbuild +++ b/examples/10_openssl_server/main/Kconfig.projbuild @@ -12,4 +12,4 @@ config WIFI_PASSWORD help WiFi password (WPA or WPA2) for the example to use. -endmenu \ No newline at end of file +endmenu diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c old mode 100644 new mode 100755 index 756c1407f5..1eea2110ce --- a/examples/10_openssl_server/main/openssl_server.c +++ b/examples/10_openssl_server/main/openssl_server.c @@ -43,7 +43,8 @@ const static char *TAG = "Openssl_demo"; "OpenSSL demo\r\n" \ "OpenSSL server demo!\r\n" \ "\r\n" \ - "\r\n" + "\r\n" \ + "\r\n" static void openssl_demo_thread(void *p) { @@ -70,7 +71,7 @@ static void openssl_demo_thread(void *p) const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; ESP_LOGI(TAG, "SSL server context create ......"); - ctx = SSL_CTX_new(SSLv3_server_method()); + ctx = SSL_CTX_new(TLS_server_method()); if (!ctx) { ESP_LOGI(TAG, "failed"); goto failed1; @@ -155,14 +156,21 @@ reconnect: if (ret <= 0) { break; } - if (strstr(recv_buf, "GET / HTTP/1.1")) { - SSL_write(ssl, send_data, send_bytes); + ESP_LOGI(TAG, "SSL read: %s", recv_buf); + if (strstr(recv_buf, "GET ") && + strstr(recv_buf, " HTTP/1.1")) { + ESP_LOGI(TAG, "SSL get matched message") + ESP_LOGI(TAG, "SSL write message") + ret = SSL_write(ssl, send_data, send_bytes); + if (ret > 0) { + ESP_LOGI(TAG, "OK") + } else { + ESP_LOGI(TAG, "error") + } break; } } while (1); - ESP_LOGI(TAG, "result %d", ret); - SSL_shutdown(ssl); failed5: close(new_socket); diff --git a/examples/10_openssl_server/main/openssl_server.h b/examples/10_openssl_server/main/openssl_server.h old mode 100644 new mode 100755 index 5f49de35f2..51708535f5 --- a/examples/10_openssl_server/main/openssl_server.h +++ b/examples/10_openssl_server/main/openssl_server.h @@ -7,8 +7,10 @@ CONDITIONS OF ANY KIND, either express or implied. */ -#ifndef _OPENSSL_DEMO_H_ -#define _OPENSSL_DEMO_H_ +#ifndef _OPENSSL_SERVER_H_ +#define _OPENSSL_SERVER_H_ + +#include "sdkconfig.h" /* The examples use simple WiFi configuration that you can set via 'make menuconfig'. diff --git a/examples/11_rmt_nec_tx_rx/README.md b/examples/11_rmt_nec_tx_rx/README.md new file mode 100644 index 0000000000..d578600d01 --- /dev/null +++ b/examples/11_rmt_nec_tx_rx/README.md @@ -0,0 +1,6 @@ +# Example: rmt_nec_tx_rx + +This example uses the remote control (RMT) peripheral to transmit and receive codes for the NEC infrared remote protocol. + +Configuration (pin numbers, etc.) can be modified in top of the main/infrared_nec.c file. + diff --git a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c index d2f7b091fa..ea42502749 100644 --- a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c +++ b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c @@ -352,7 +352,7 @@ void rmt_nec_tx_task() rmt_wait_tx_done(channel); //before we free the data, make sure sending is already done. free(item); - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); } vTaskDelete(NULL); } diff --git a/examples/12_blufi/Makefile b/examples/12_blufi/Makefile index 7e7548444f..9c80f26c1a 100644 --- a/examples/12_blufi/Makefile +++ b/examples/12_blufi/Makefile @@ -8,11 +8,3 @@ PROJECT_NAME := blufi_demo COMPONENT_ADD_INCLUDEDIRS := components/include include $(IDF_PATH)/make/project.mk - -# Copy some defaults into the sdkconfig by default -# so BT stack is enabled -sdkconfig: sdkconfig.defaults - $(Q) cp $< $@ - -menuconfig: sdkconfig -defconfig: sdkconfig diff --git a/examples/12_blufi/components/blufi/blufi.c b/examples/12_blufi/components/blufi/blufi.c deleted file mode 100644 index 5a1c543b50..0000000000 --- a/examples/12_blufi/components/blufi/blufi.c +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2015-2016 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. - -/*************************************************************** -* -* This file is for gatt server device. It instantiates BATTERY -* sevice. It can be scanned and connected by central device, -* and the client will get the BAS value. It calls the API bta -* layer provides. -* -****************************************************************/ - -#include -#include -#include -#include - -#include "controller.h" - -#include "bt_trace.h" -#include "bt_types.h" - -#include "blufi.h" - -#include "esp_bt_defs.h" -#include "esp_bt_main.h" -#include "esp_blufi_api.h" - -extern void wifi_set_blue_config(char *ssid, char *passwd); - -#define HEADER_SSID "ssid" -#define HEADER_PASSWD "passwd" -#define HEADER_CONFIRM "confirm" - -static char tmp_ssid[32 + 1]; -static char tmp_passwd[64 + 1]; - -static void blufi_data_recv(uint8_t *data, int len) -{ - char *p = NULL; - LOG_DEBUG("the data is:%s\n", data); - - p = strstr((char *)data, HEADER_SSID); - if (p) { - LOG_ERROR("SSID: %s\n", p + strlen(HEADER_SSID) + 1); - strcpy(tmp_ssid, p + strlen(HEADER_SSID) + 1); - } - p = strstr((char *)data, HEADER_PASSWD); - if (p) { - LOG_ERROR("PASSWORD: %s\n", p + strlen(HEADER_PASSWD) + 1); - strcpy(tmp_passwd, p + strlen(HEADER_PASSWD) + 1); - } - p = strstr((char *)data, HEADER_CONFIRM); - if (p) { - LOG_ERROR("CONFIRM\n"); - wifi_set_blue_config(tmp_ssid, tmp_passwd); - } - -} - -static void blufi_callback(uint32_t event, void *param) -{ - /* actually, should post to blufi_task handle the procedure, - * now, as a demo, we do simplely */ - switch (event) { - case ESP_BLUFI_EVENT_INIT_FINISH: - LOG_ERROR("blufi init finish\n"); - break; - case ESP_BLUFI_EVENT_RECV_DATA: { - LOG_DEBUG("blufi recv data\n"); - esp_blufi_cb_param_t *blufi_param = (esp_blufi_cb_param_t *)param; - blufi_data_recv(blufi_param->recv_data.data, blufi_param->recv_data.data_len); - break; - } - default: - break; - } -} - -static esp_err_t blufi_startup_in_blufi_task(void *arg) -{ - /*set connectable,discoverable, pairable and paired only modes of local device*/ - tBTA_DM_DISC disc_mode = BTA_DM_BLE_GENERAL_DISCOVERABLE; - tBTA_DM_CONN conn_mode = BTA_DM_BLE_CONNECTABLE; - BTA_DmSetVisibility(disc_mode, conn_mode, (uint8_t)BTA_DM_NON_PAIRABLE, (uint8_t)BTA_DM_CONN_ALL); - - esp_blufi_register_callback(blufi_callback); - esp_blufi_profile_init(); - - return ESP_OK; -} - - -static void blufi_startup(void) -{ - blufi_transfer_context(blufi_startup_in_blufi_task, NULL); -} - -esp_err_t blufi_enable(void *arg) -{ - esp_err_t err; - - BTM_SetTraceLevel(BT_TRACE_LEVEL_ERROR); - - err = esp_enable_bluetooth(); - if (err) { - LOG_ERROR("%s failed\n", __func__); - return err; - } - blufi_startup(); - vTaskDelay(1000 / portTICK_PERIOD_MS); - - return err; -} - -esp_err_t blufi_disable(void *arg) -{ - esp_err_t err; - - err = esp_disable_bluetooth(); - - if (arg) { - ((void (*)(void))arg)(); - } - - return err; -} diff --git a/examples/12_blufi/components/blufi/blufi_task.c b/examples/12_blufi/components/blufi/blufi_task.c deleted file mode 100644 index cda66c0511..0000000000 --- a/examples/12_blufi/components/blufi/blufi_task.c +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2015-2016 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. - -#include -#include -#include -#include - -#include "gki.h" -#include "bt_defs.h" -#include "bt_trace.h" -#include "bt_types.h" -#include "allocator.h" - -#include "bta_api.h" -#include "bta_gatt_api.h" - -#include "controller.h" - -#include "hash_map.h" -#include "hash_functions.h" -#include "alarm.h" -#include "thread.h" - -#include "blufi.h" -#include "blufi_adv.h" - -xQueueHandle xBlufiTaskQueue; -xTaskHandle xBlufiTaskHandle; - -extern void ble_server_test(void); - -static void blufi_task(void *arg) -{ - BtTaskEvt_t e; - - for (;;) { - if (pdTRUE == xQueueReceive(xBlufiTaskQueue, &e, (portTickType)portMAX_DELAY)) { - switch (e.sig) { - case BLUFI_SIG_SWITCH_CONTEXT: - if (e.cb) { - ((BtTaskCb_t)e.cb)(e.arg); - } - break; - default: - break; - } - } - } -} - -static esp_err_t blufi_task_post(uint32_t sig, void *par, void *cb, void *arg) -{ - BtTaskEvt_t evt; - - evt.sig = sig; - evt.par = par; - evt.cb = cb; - evt.arg = arg; - - if (xQueueSend(xBlufiTaskQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { - LOG_ERROR("Blufi Post failed\n"); - return ESP_FAIL; - } - - return ESP_OK; -} - -esp_err_t blufi_transfer_context(blufi_task_cb_t cb, void *arg) -{ - LOG_DEBUG("%s cb %08x, arg %u\n", __func__, (uint32_t)cb, (uint32_t)arg); - - return blufi_task_post(BLUFI_SIG_SWITCH_CONTEXT, 0, cb, arg); -} - -static void blufi_task_deinit(void) -{ - vTaskDelete(xBlufiTaskHandle); - vQueueDelete(xBlufiTaskQueue); -} - - -static void blufi_task_init(void) -{ - xBlufiTaskQueue = xQueueCreate(5, sizeof(BtTaskEvt_t)); - xTaskCreate(blufi_task, "BlUFI", 4096, NULL, configMAX_PRIORITIES - 3, xBlufiTaskHandle); -} - -void blufi_init(void) -{ - blufi_task_init(); - blufi_transfer_context(blufi_enable, NULL); -} - -void blufi_deinit(void) -{ - blufi_transfer_context(blufi_disable, blufi_task_deinit); -} - diff --git a/examples/12_blufi/main/blufi_demo.h b/examples/12_blufi/main/blufi_demo.h new file mode 100644 index 0000000000..c5bf55c7d5 --- /dev/null +++ b/examples/12_blufi/main/blufi_demo.h @@ -0,0 +1,17 @@ +#ifndef __BLUFI_DEMO_H__ +#define __BLUFI_DEMO_H__ + + +#define BLUFI_DEMO_TAG "BLUFI_DEMO" +#define BLUFI_INFO(fmt, ...) ESP_LOGI(BLUFI_DEMO_TAG, fmt, ##__VA_ARGS__) +#define BLUFI_ERROR(fmt, ...) ESP_LOGE(BLUFI_DEMO_TAG, fmt, ##__VA_ARGS__) + +void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free); +int blufi_aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len); +int blufi_aes_decrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len); +uint16_t blufi_crc_checksum(uint8_t iv8, uint8_t *data, int len); + +int blufi_security_init(void); +void blufi_security_deinit(void); + +#endif /* __BLUFI_DEMO_H__ */ diff --git a/examples/12_blufi/main/blufi_main.c b/examples/12_blufi/main/blufi_main.c new file mode 100644 index 0000000000..a95db66eb5 --- /dev/null +++ b/examples/12_blufi/main/blufi_main.c @@ -0,0 +1,341 @@ +// Copyright 2015-2016 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. + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "bt.h" + +#include "esp_blufi_api.h" +#include "esp_bt_defs.h" +#include "esp_gap_ble_api.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" +#include "blufi_demo.h" + +static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param); + +#define BLUFI_DEVICE_NAME "BLUFI_DEVICE" +static uint8_t blufi_service_uuid128[32] = { + /* LSB <--------------------------------------------------------------------------------> MSB */ + //first uuid, 16bit, [12],[13] is the value + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, +}; + +//static uint8_t test_manufacturer[TEST_MANUFACTURER_DATA_LEN] = {0x12, 0x23, 0x45, 0x56}; +static esp_ble_adv_data_t blufi_adv_data = { + .set_scan_rsp = false, + .include_name = true, + .include_txpower = true, + .min_interval = 0x100, + .max_interval = 0x100, + .appearance = 0x00, + .manufacturer_len = 0, + .p_manufacturer_data = NULL, + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = 16, + .p_service_uuid = blufi_service_uuid128, + .flag = 0x6, +}; + +static esp_ble_adv_params_t blufi_adv_params = { + .adv_int_min = 0x100, + .adv_int_max = 0x100, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + //.peer_addr = + //.peer_addr_type = + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, +}; + +#define WIFI_LIST_NUM 10 + +static wifi_config_t sta_config; +static wifi_config_t ap_config; + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +/* store the station info for send back to phone */ +static bool gl_sta_connected = false; +static uint8_t gl_sta_bssid[6]; +static uint8_t gl_sta_ssid[32]; +static int gl_sta_ssid_len; + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + wifi_mode_t mode; + + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: { + esp_blufi_extra_info_t info; + + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + esp_wifi_get_mode(&mode); + + memset(&info, 0, sizeof(esp_blufi_extra_info_t)); + memcpy(info.sta_bssid, gl_sta_bssid, 6); + info.sta_bssid_set = true; + info.sta_ssid = gl_sta_ssid; + info.sta_ssid_len = gl_sta_ssid_len; + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info); + break; + } + case SYSTEM_EVENT_STA_CONNECTED: + gl_sta_connected = true; + memcpy(gl_sta_bssid, event->event_info.connected.bssid, 6); + memcpy(gl_sta_ssid, event->event_info.connected.ssid, event->event_info.connected.ssid_len); + gl_sta_ssid_len = event->event_info.connected.ssid_len; + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + gl_sta_connected = false; + memset(gl_sta_ssid, 0, 32); + memset(gl_sta_bssid, 0, 6); + gl_sta_ssid_len = 0; + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_AP_START: + esp_wifi_get_mode(&mode); + + /* TODO: get config or information of softap, then set to report extra_info */ + if (gl_sta_connected) { + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, NULL); + } else { + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL); + } + break; + default: + break; + } + return ESP_OK; +} + +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +static esp_blufi_callbacks_t blufi_callbacks = { + .event_cb = blufi_event_callback, + .negotiate_data_handler = blufi_dh_negotiate_data_handler, + .encrypt_func = blufi_aes_encrypt, + .decrypt_func = blufi_aes_decrypt, + .checksum_func = blufi_crc_checksum, +}; + +static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param) +{ + /* actually, should post to blufi_task handle the procedure, + * now, as a demo, we do simplely */ + switch (event) { + case ESP_BLUFI_EVENT_INIT_FINISH: + BLUFI_INFO("BLUFI init finish\n"); + + esp_ble_gap_set_device_name(BLUFI_DEVICE_NAME); + esp_ble_gap_config_adv_data(&blufi_adv_data); + break; + case ESP_BLUFI_EVENT_DEINIT_FINISH: + BLUFI_INFO("BLUFI init finish\n"); + break; + case ESP_BLUFI_EVENT_BLE_CONNECT: + BLUFI_INFO("BLUFI ble connect\n"); + esp_ble_gap_stop_advertising(); + blufi_security_deinit(); + blufi_security_init(); + break; + case ESP_BLUFI_EVENT_BLE_DISCONNECT: + BLUFI_INFO("BLUFI ble disconnect\n"); + esp_ble_gap_start_advertising(&blufi_adv_params); + break; + case ESP_BLUFI_EVENT_SET_WIFI_OPMODE: + BLUFI_INFO("BLUFI Set WIFI opmode %d\n", param->wifi_mode.op_mode); + ESP_ERROR_CHECK( esp_wifi_set_mode(param->wifi_mode.op_mode) ); + break; + case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP: + BLUFI_INFO("BLUFI requset wifi connect to AP\n"); + esp_wifi_connect(); + break; + case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP: + BLUFI_INFO("BLUFI requset wifi disconnect from AP\n"); + esp_wifi_disconnect(); + break; + case ESP_BLUFI_EVENT_GET_WIFI_STATUS: { + wifi_mode_t mode; + esp_blufi_extra_info_t info; + + esp_wifi_get_mode(&mode); + + if (gl_sta_connected ) { + memset(&info, 0, sizeof(esp_blufi_extra_info_t)); + memcpy(info.sta_bssid, gl_sta_bssid, 6); + info.sta_bssid_set = true; + info.sta_ssid = gl_sta_ssid; + info.sta_ssid_len = gl_sta_ssid_len; + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info); + } else { + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL); + } + BLUFI_INFO("BLUFI get wifi status from AP\n"); + + break; + } + case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA: + /* TODO */ + break; + case ESP_BLUFI_EVENT_RECV_STA_BSSID: + memcpy(sta_config.sta.bssid, param->sta_bssid.bssid, 6); + sta_config.sta.bssid_set = 1; + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + BLUFI_INFO("Recv STA BSSID %s\n", sta_config.sta.ssid); + break; + case ESP_BLUFI_EVENT_RECV_STA_SSID: + strncpy((char *)sta_config.sta.ssid, (char *)param->sta_ssid.ssid, param->sta_ssid.ssid_len); + sta_config.sta.ssid[param->sta_ssid.ssid_len] = '\0'; + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + BLUFI_INFO("Recv STA SSID %s\n", sta_config.sta.ssid); + break; + case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + strncpy((char *)sta_config.sta.password, (char *)param->sta_passwd.passwd, param->sta_passwd.passwd_len); + sta_config.sta.password[param->sta_passwd.passwd_len] = '\0'; + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + BLUFI_INFO("Recv STA PASSWORD %s\n", sta_config.sta.password); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + strncpy((char *)ap_config.ap.ssid, (char *)param->softap_ssid.ssid, param->softap_ssid.ssid_len); + ap_config.ap.ssid_len = param->softap_ssid.ssid_len; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP SSID %s, ssid len %d\n", ap_config.ap.ssid, ap_config.ap.ssid_len); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + strncpy((char *)ap_config.ap.password, (char *)param->softap_passwd.passwd, param->softap_passwd.passwd_len); + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP PASSWORD %s\n", ap_config.ap.password); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM: + if (param->softap_max_conn_num.max_conn_num > 4) { + return; + } + ap_config.ap.max_connection = param->softap_max_conn_num.max_conn_num; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP MAX CONN NUM %d\n", ap_config.ap.max_connection); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE: + if (param->softap_auth_mode.auth_mode >= WIFI_AUTH_MAX) { + return; + } + ap_config.ap.authmode = param->softap_auth_mode.auth_mode; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP AUTH MODE %d\n", ap_config.ap.authmode); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL: + if (param->softap_channel.channel > 13) { + return; + } + ap_config.ap.channel = param->softap_channel.channel; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP CHANNEL %d\n", ap_config.ap.channel); + break; + case ESP_BLUFI_EVENT_RECV_USERNAME: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_CA_CERT: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + /* Not handle currently */ + break;; + case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + /* Not handle currently */ + break; + default: + break; + } +} + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + switch (event) { + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + esp_ble_gap_start_advertising(&blufi_adv_params); + break; + default: + break; + } +} + +void app_main() +{ + esp_err_t ret; + + nvs_flash_init(); + initialise_wifi(); + + esp_bt_controller_init(); + + ret = esp_bluedroid_init(); + if (ret) { + BLUFI_ERROR("%s init bluedroid failed\n", __func__); + return; + } + + ret = esp_bluedroid_enable(); + if (ret) { + BLUFI_ERROR("%s init bluedroid failed\n", __func__); + return; + } + + BLUFI_INFO("BD ADDR: "ESP_BD_ADDR_STR"\n", ESP_BD_ADDR_HEX(esp_bt_dev_get_address())); + + BLUFI_INFO("BLUFI VERSION %04x\n", esp_blufi_get_version()); + + blufi_security_init(); + esp_ble_gap_register_callback(gap_event_handler); + + esp_blufi_register_callbacks(&blufi_callbacks); + esp_blufi_profile_init(); +} diff --git a/examples/12_blufi/main/blufi_security.c b/examples/12_blufi/main/blufi_security.c new file mode 100644 index 0000000000..59d889a52d --- /dev/null +++ b/examples/12_blufi/main/blufi_security.c @@ -0,0 +1,205 @@ +// Copyright 2015-2016 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. + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "bt.h" + +#include "esp_blufi_api.h" +#include "esp_bt_defs.h" +#include "esp_gap_ble_api.h" +#include "esp_bt_main.h" +#include "blufi_demo.h" + +#include "mbedtls/aes.h" +#include "mbedtls/dhm.h" +#include "mbedtls/md5.h" +#include "rom/crc.h" + +/* + The SEC_TYPE_xxx is for self-defined packet data type in the procedure of "BLUFI negotiate key" + If user use other negotiation procedure to exchange(or generate) key, should redefine the type by yourself. + */ +#define SEC_TYPE_DH_PARAM_LEN 0x00 +#define SEC_TYPE_DH_PARAM_DATA 0x01 +#define SEC_TYPE_DH_P 0x02 +#define SEC_TYPE_DH_G 0x03 +#define SEC_TYPE_DH_PUBLIC 0x04 + + +struct blufi_security { +#define DH_SELF_PUB_KEY_LEN 128 +#define DH_SELF_PUB_KEY_BIT_LEN (DH_SELF_PUB_KEY_LEN * 8) + uint8_t self_public_key[DH_SELF_PUB_KEY_LEN]; +#define SHARE_KEY_LEN 128 +#define SHARE_KEY_BIT_LEN (SHARE_KEY_LEN * 8) + uint8_t share_key[SHARE_KEY_LEN]; + size_t share_len; +#define PSK_LEN 16 + uint8_t psk[PSK_LEN]; + uint8_t *dh_param; + int dh_param_len; + uint8_t iv[16]; + mbedtls_dhm_context dhm; + mbedtls_aes_context aes; +}; +static struct blufi_security *blufi_sec; + +static int myrand( void *rng_state, unsigned char *output, size_t len ) +{ + size_t i; + + for( i = 0; i < len; ++i ) + output[i] = esp_random(); + + return( 0 ); +} + +void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free) +{ + int ret; + uint8_t type = data[0]; + + if (blufi_sec == NULL) { + BLUFI_ERROR("BLUFI Security is not initialized"); + return; + } + + switch (type) { + case SEC_TYPE_DH_PARAM_LEN: + blufi_sec->dh_param_len = ((data[1]<<8)|data[2]); + if (blufi_sec->dh_param) { + free(blufi_sec->dh_param); + } + blufi_sec->dh_param = (uint8_t *)malloc(blufi_sec->dh_param_len); + if (blufi_sec->dh_param == NULL) { + return; + } + break; + case SEC_TYPE_DH_PARAM_DATA: + + memcpy(blufi_sec->dh_param, &data[1], blufi_sec->dh_param_len); + + ret = mbedtls_dhm_read_params(&blufi_sec->dhm, &blufi_sec->dh_param, &blufi_sec->dh_param[blufi_sec->dh_param_len]); + if (ret) { + BLUFI_ERROR("%s read param failed %d\n", __func__, ret); + return; + } + + ret = mbedtls_dhm_make_public(&blufi_sec->dhm, (int) mbedtls_mpi_size( &blufi_sec->dhm.P ), blufi_sec->self_public_key, blufi_sec->dhm.len, myrand, NULL); + if (ret) { + BLUFI_ERROR("%s make public failed %d\n", __func__, ret); + return; + } + + mbedtls_dhm_calc_secret( &blufi_sec->dhm, + blufi_sec->share_key, + SHARE_KEY_BIT_LEN, + &blufi_sec->share_len, + NULL, NULL); + + mbedtls_md5(blufi_sec->share_key, blufi_sec->share_len, blufi_sec->psk); + + mbedtls_aes_setkey_enc(&blufi_sec->aes, blufi_sec->psk, 128); + mbedtls_aes_setkey_dec(&blufi_sec->aes, blufi_sec->psk, 128); + + /* alloc output data */ + *output_data = &blufi_sec->self_public_key[0]; + *output_len = blufi_sec->dhm.len; + *need_free = false; + break; + case SEC_TYPE_DH_P: + break; + case SEC_TYPE_DH_G: + break; + case SEC_TYPE_DH_PUBLIC: + break; + } +} + +int blufi_aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len) +{ + int ret; + size_t iv_offset = 0; + uint8_t iv0[16]; + + memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv)); + iv0[0] = iv8; /* set iv8 as the iv0[0] */ + + ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_ENCRYPT, crypt_len, &iv_offset, iv0, crypt_data, crypt_data); + if (ret) { + return -1; + } + + return crypt_len; +} + +int blufi_aes_decrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len) +{ + int ret; + size_t iv_offset = 0; + uint8_t iv0[16]; + + memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv)); + iv0[0] = iv8; /* set iv8 as the iv0[0] */ + + ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_DECRYPT, crypt_len, &iv_offset, iv0, crypt_data, crypt_data); + if (ret) { + return -1; + } + + return crypt_len; +} + +uint16_t blufi_crc_checksum(uint8_t iv8, uint8_t *data, int len) +{ + /* This iv8 ignore, not used */ + return crc16_be(0, data, len); +} + +esp_err_t blufi_security_init(void) +{ + blufi_sec = (struct blufi_security *)malloc(sizeof(struct blufi_security)); + if (blufi_sec == NULL) { + return ESP_FAIL; + } + + memset(blufi_sec, 0x0, sizeof(struct blufi_security)); + + mbedtls_dhm_init(&blufi_sec->dhm); + mbedtls_aes_init(&blufi_sec->aes); + + memset(blufi_sec->iv, 0x0, 16); + return 0; +} + +void blufi_security_deinit(void) +{ + mbedtls_dhm_free(&blufi_sec->dhm); + mbedtls_aes_free(&blufi_sec->aes); + + memset(blufi_sec, 0x0, sizeof(struct blufi_security)); + + free(blufi_sec); + blufi_sec = NULL; +} diff --git a/examples/12_blufi/main/demo_main.c b/examples/12_blufi/main/demo_main.c deleted file mode 100644 index f1c3807741..0000000000 --- a/examples/12_blufi/main/demo_main.c +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2015-2016 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. - -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event_loop.h" -#include "esp_log.h" -#include "nvs_flash.h" -#include "bt.h" -#include "bta_api.h" - -#include "esp_blufi_api.h" -#include "esp_bt_defs.h" -#include "esp_bt_main.h" -#include "blufi.h" - -#define WIFI_LIST_NUM 10 - -/* FreeRTOS event group to signal when we are connected & ready to make a request */ -static EventGroupHandle_t wifi_event_group; - -/* The event group allows multiple bits for each event, - but we only care about one event - are we connected - to the AP with an IP? */ -const int CONNECTED_BIT = BIT0; - - - -static wifi_config_t sta_config; - -static char tmp_ssid[33]; -static char tmp_passwd[33]; -static bool confirm = false; - -void wifi_set_blue_config(char *ssid, char *passwd) -{ - memset(tmp_ssid, 0, 33); - memset(tmp_passwd, 0, 33); - strcpy(tmp_ssid, ssid); - strcpy(tmp_passwd, passwd); - confirm = true; - LOG_DEBUG("confirm true\n"); -} - -static esp_err_t event_handler(void *ctx, system_event_t *event) -{ - switch (event->event_id) { - case SYSTEM_EVENT_STA_START: - esp_wifi_connect(); - break; - case SYSTEM_EVENT_STA_GOT_IP: - xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - esp_blufi_send_config_state(ESP_BLUFI_CONFIG_OK); - esp_disable_bluetooth(); //close bluetooth function - //esp_deinit_bluetooth(); //free bluetooth resource - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - /* This is a workaround as ESP32 WiFi libs don't currently - auto-reassociate. */ - esp_wifi_connect(); - xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); - break; - default: - break; - } - return ESP_OK; -} - -static void initialise_wifi(void) -{ - tcpip_adapter_init(); - wifi_event_group = xEventGroupCreate(); - ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK( esp_wifi_start() ); -} - - -void wifiTestTask(void *pvParameters) -{ - esp_err_t ret; - - while (1) { - vTaskDelay(1000 / portTICK_PERIOD_MS); - if (confirm) { - confirm = false; - - strcpy(sta_config.sta.ssid, tmp_ssid); - strcpy(sta_config.sta.password, tmp_passwd); - sta_config.sta.bssid_set = 0; - - ret = esp_wifi_disconnect(); - LOG_INFO("esp_wifi config\n"); - esp_wifi_set_config(WIFI_IF_STA, &sta_config); - LOG_INFO("esp_wifi connect\n"); - ret = esp_wifi_connect(); - if (ret != ESP_OK) { - LOG_ERROR("esp_wifi connect failed\n"); - esp_blufi_send_config_state(ESP_BLUFI_CONFIG_FAILED); - } - } - } -} - -void app_main() -{ - esp_err_t ret; - - nvs_flash_init(); - initialise_wifi(); - - //vTaskDelay(3000 / portTICK_PERIOD_MS); - - bt_controller_init(); - xTaskCreatePinnedToCore(&wifiTestTask, "wifiTestTask", 2048, NULL, 20, NULL, 0); - - LOG_ERROR("%s init bluetooth\n", __func__); - ret = esp_init_bluetooth(); - if (ret) { - LOG_ERROR("%s init bluetooth failed\n", __func__); - return; - } - blufi_init(); -} diff --git a/examples/12_blufi/sdkconfig.defaults b/examples/12_blufi/sdkconfig.defaults index e435f383c8..dcf4ad2c2d 100644 --- a/examples/12_blufi/sdkconfig.defaults +++ b/examples/12_blufi/sdkconfig.defaults @@ -5,10 +5,3 @@ # BT config # CONFIG_BT_ENABLED=y - -# -# ESP32-specific config -# -CONFIG_ESP32_ENABLE_STACK_BT=y -# CONFIG_ESP32_ENABLE_STACK_NONE is not set -CONFIG_MEMMAP_BT=y diff --git a/examples/13_timer_group/README.md b/examples/13_timer_group/README.md new file mode 100644 index 0000000000..0be2c847a8 --- /dev/null +++ b/examples/13_timer_group/README.md @@ -0,0 +1,3 @@ +# Example: timer_group + +This example uses the timer group driver to generate timer interrupts at two specified alarm intervals. diff --git a/examples/14_gatt_server/Makefile b/examples/14_gatt_server/Makefile index d7732bd280..2f76e60b60 100644 --- a/examples/14_gatt_server/Makefile +++ b/examples/14_gatt_server/Makefile @@ -8,11 +8,3 @@ PROJECT_NAME := gatt_server_demos COMPONENT_ADD_INCLUDEDIRS := components/include include $(IDF_PATH)/make/project.mk - -# Copy some defaults into the sdkconfig by default -# so BT stack is enabled -sdkconfig: sdkconfig.defaults - $(Q) cp $< $@ - -menuconfig: sdkconfig -defconfig: sdkconfig diff --git a/examples/14_gatt_server/main/gatts_demo.c b/examples/14_gatt_server/main/gatts_demo.c index 48576d362f..98feaa2240 100644 --- a/examples/14_gatt_server/main/gatts_demo.c +++ b/examples/14_gatt_server/main/gatts_demo.c @@ -30,14 +30,37 @@ #include "esp_bt_main.h" #include "esp_bt_main.h" -#define GATTS_SERVICE_UUID_TEST 0x00FF -#define GATTS_CHAR_UUID_TEST 0xFF01 -#define GATTS_DESCR_UUID_TEST 0x3333 -#define APP_ID_TEST 0x18 -#define GATTS_NUM_HANDLE_TEST 4 -#define TEST_DEVICE_NAME "ESP_GATTS_DEMO" +#define GATTS_TAG "GATTS_DEMO" +///Declare the static function +static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); +static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + +#define GATTS_SERVICE_UUID_TEST_A 0x00FF +#define GATTS_CHAR_UUID_TEST_A 0xFF01 +#define GATTS_DESCR_UUID_TEST_A 0x3333 +#define GATTS_NUM_HANDLE_TEST_A 4 + +#define GATTS_SERVICE_UUID_TEST_B 0x00EE +#define GATTS_CHAR_UUID_TEST_B 0xEE01 +#define GATTS_DESCR_UUID_TEST_B 0x2222 +#define GATTS_NUM_HANDLE_TEST_B 4 + +#define TEST_DEVICE_NAME "ESP_GATTS_DEMO" #define TEST_MANUFACTURER_DATA_LEN 17 + +#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 + +uint8_t char1_str[] ={0x11,0x22,0x33}; + +esp_attr_value_t gatts_demo_char1_val = +{ + .attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX, + .attr_len = sizeof(char1_str), + .attr_value = char1_str, +}; + + static uint8_t test_service_uuid128[32] = { /* LSB <--------------------------------------------------------------------------------> MSB */ //first uuid, 16bit, [12],[13] is the value @@ -60,7 +83,7 @@ static esp_ble_adv_data_t test_adv_data = { .p_service_data = NULL, .service_uuid_len = 32, .p_service_uuid = test_service_uuid128, - .flag = 0x2, + .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), }; static esp_ble_adv_params_t test_adv_params = { @@ -74,8 +97,13 @@ static esp_ble_adv_params_t test_adv_params = { .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; -struct gatts_test_inst { - uint16_t gatt_if; +#define PROFILE_NUM 2 +#define PROFILE_A_APP_ID 0 +#define PROFILE_B_APP_ID 1 + +struct gatts_profile_inst { + esp_gatts_cb_t gatts_cb; + uint16_t gatts_if; uint16_t app_id; uint16_t conn_id; uint16_t service_handle; @@ -87,12 +115,21 @@ struct gatts_test_inst { uint16_t descr_handle; esp_bt_uuid_t descr_uuid; }; -static struct gatts_test_inst gl_test; -static void gap_event_handler(uint32_t event, void *param) +/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ +static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = { + [PROFILE_A_APP_ID] = { + .gatts_cb = gatts_profile_a_event_handler, + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, + [PROFILE_B_APP_ID] = { + .gatts_cb = gatts_profile_b_event_handler, /* This demo does not implement, similar as profile A */ + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - LOG_ERROR("GAP_EVT, event %d\n", event); - switch (event) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: esp_ble_gap_start_advertising(&test_adv_params); @@ -102,42 +139,38 @@ static void gap_event_handler(uint32_t event, void *param) } } -static void gatts_event_handler(uint32_t event, void *param) -{ - esp_ble_gatts_cb_param_t *p = (esp_ble_gatts_cb_param_t *)param; - +static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { switch (event) { case ESP_GATTS_REG_EVT: - LOG_INFO("REGISTER_APP_EVT, status %d, gatt_if %d, app_id %d\n", p->reg.status, p->reg.gatt_if, p->reg.app_id); - gl_test.gatt_if = p->reg.gatt_if; - gl_test.service_id.is_primary = true; - gl_test.service_id.id.inst_id = 0x00; - gl_test.service_id.id.uuid.len = ESP_UUID_LEN_16; - gl_test.service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST; + ESP_LOGI(GATTS_TAG, "REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); + gl_profile_tab[PROFILE_A_APP_ID].service_id.is_primary = true; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A; esp_ble_gap_set_device_name(TEST_DEVICE_NAME); esp_ble_gap_config_adv_data(&test_adv_data); - esp_ble_gatts_create_service(gl_test.gatt_if, &gl_test.service_id, GATTS_NUM_HANDLE_TEST); + esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_A); break; case ESP_GATTS_READ_EVT: { - LOG_INFO("GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", p->read.conn_id, p->read.trans_id, p->read.handle); + ESP_LOGI(GATTS_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); esp_gatt_rsp_t rsp; memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); - rsp.attr_value.handle = p->read.handle; + rsp.attr_value.handle = param->read.handle; rsp.attr_value.len = 4; rsp.attr_value.value[0] = 0xde; rsp.attr_value.value[1] = 0xed; rsp.attr_value.value[2] = 0xbe; rsp.attr_value.value[3] = 0xef; - esp_ble_gatts_send_response(p->read.conn_id, p->read.trans_id, + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp); break; } case ESP_GATTS_WRITE_EVT: { - LOG_INFO("GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", p->write.conn_id, p->write.trans_id, p->write.handle); - LOG_INFO("GATT_WRITE_EVT, value len %d, value %08x\n", p->write.len, *(uint32_t *)p->write.value); - esp_ble_gatts_send_response(p->write.conn_id, p->write.trans_id, ESP_GATT_OK, NULL); + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value %08x\n", param->write.len, *(uint32_t *)param->write.value); + esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); break; } case ESP_GATTS_EXEC_WRITE_EVT: @@ -146,48 +179,58 @@ static void gatts_event_handler(uint32_t event, void *param) case ESP_GATTS_UNREG_EVT: break; case ESP_GATTS_CREATE_EVT: - LOG_INFO("CREATE_SERVICE_EVT, status %d, gatt_if %d, service_handle %d\n", p->create.status, p->create.gatt_if, p->create.service_handle); - gl_test.service_handle = p->create.service_handle; - gl_test.char_uuid.len = ESP_UUID_LEN_16; - gl_test.char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST; + ESP_LOGI(GATTS_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); + gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_A; - esp_ble_gatts_start_service(gl_test.service_handle); + esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle); - esp_ble_gatts_add_char(gl_test.service_handle, &gl_test.char_uuid, + esp_ble_gatts_add_char(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].char_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, - ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY); + ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY, + &gatts_demo_char1_val, NULL); break; case ESP_GATTS_ADD_INCL_SRVC_EVT: break; - case ESP_GATTS_ADD_CHAR_EVT: - LOG_INFO("ADD_CHAR_EVT, status %d, gatt_if %d, attr_handle %d, service_handle %d\n", - p->add_char.status, p->add_char.gatt_if, p->add_char.attr_handle, p->add_char.service_handle); + case ESP_GATTS_ADD_CHAR_EVT: { + uint16_t length = 0; + const uint8_t *prf_char; - gl_test.char_handle = p->add_char.attr_handle; - gl_test.descr_uuid.len = ESP_UUID_LEN_16; - gl_test.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; - esp_ble_gatts_add_char_descr(gl_test.service_handle, &gl_test.descr_uuid, - ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE); + ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); + gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; + esp_ble_gatts_get_attr_value(param->add_char.attr_handle, &length, &prf_char); + + ESP_LOGI(GATTS_TAG, "the gatts demo char length = %x\n", length); + for(int i = 0; i < length; i++){ + ESP_LOGI(GATTS_TAG, "prf_char[%x] =%x\n",i,prf_char[i]); + } + esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, NULL, NULL); break; + } case ESP_GATTS_ADD_CHAR_DESCR_EVT: - LOG_INFO("ADD_DESCR_EVT, status %d, gatt_if %d, attr_handle %d, service_handle %d\n", - p->add_char.status, p->add_char.gatt_if, p->add_char.attr_handle, p->add_char.service_handle); + ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); break; case ESP_GATTS_DELETE_EVT: break; case ESP_GATTS_START_EVT: - LOG_INFO("SERVICE_START_EVT, status %d, gatt_if %d, service_handle %d\n", - p->start.status, p->start.gatt_if, p->start.service_handle); + ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, status %d, service_handle %d\n", + param->start.status, param->start.service_handle); break; case ESP_GATTS_STOP_EVT: break; case ESP_GATTS_CONNECT_EVT: - LOG_INFO("SERVICE_START_EVT, conn_id %d, gatt_if %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", - p->connect.conn_id, p->connect.gatt_if, - p->connect.remote_bda[0], p->connect.remote_bda[1], p->connect.remote_bda[2], - p->connect.remote_bda[3], p->connect.remote_bda[4], p->connect.remote_bda[5], - p->connect.is_connected); - gl_test.conn_id = p->connect.conn_id; + ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", + param->connect.conn_id, + param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2], + param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5], + param->connect.is_connected); + gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id; break; case ESP_GATTS_DISCONNECT_EVT: case ESP_GATTS_OPEN_EVT: @@ -200,26 +243,152 @@ static void gatts_event_handler(uint32_t event, void *param) } } +static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { + switch (event) { + case ESP_GATTS_REG_EVT: + ESP_LOGI(GATTS_TAG, "REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); + gl_profile_tab[PROFILE_A_APP_ID].service_id.is_primary = true; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_B; + + esp_ble_gap_set_device_name(TEST_DEVICE_NAME); + esp_ble_gap_config_adv_data(&test_adv_data); + + esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_B); + break; + case ESP_GATTS_READ_EVT: { + ESP_LOGI(GATTS_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); + esp_gatt_rsp_t rsp; + memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.len = 4; + rsp.attr_value.value[0] = 0xde; + rsp.attr_value.value[1] = 0xed; + rsp.attr_value.value[2] = 0xbe; + rsp.attr_value.value[3] = 0xef; + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, + ESP_GATT_OK, &rsp); + break; + } + case ESP_GATTS_WRITE_EVT: { + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value %08x\n", param->write.len, *(uint32_t *)param->write.value); + esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); + break; + } + case ESP_GATTS_EXEC_WRITE_EVT: + case ESP_GATTS_MTU_EVT: + case ESP_GATTS_CONF_EVT: + case ESP_GATTS_UNREG_EVT: + break; + case ESP_GATTS_CREATE_EVT: + ESP_LOGI(GATTS_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); + gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_B; + + esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle); + + esp_ble_gatts_add_char(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].char_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY, + NULL, NULL); + break; + case ESP_GATTS_ADD_INCL_SRVC_EVT: + break; + case ESP_GATTS_ADD_CHAR_EVT: + ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); + + gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; + esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + NULL, NULL); + break; + case ESP_GATTS_ADD_CHAR_DESCR_EVT: + ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); + break; + case ESP_GATTS_DELETE_EVT: + break; + case ESP_GATTS_START_EVT: + ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, status %d, service_handle %d\n", + param->start.status, param->start.service_handle); + break; + case ESP_GATTS_STOP_EVT: + break; + case ESP_GATTS_CONNECT_EVT: + ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", + param->connect.conn_id, + param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2], + param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5], + param->connect.is_connected); + gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id; + break; + case ESP_GATTS_DISCONNECT_EVT: + case ESP_GATTS_OPEN_EVT: + case ESP_GATTS_CANCEL_OPEN_EVT: + case ESP_GATTS_CLOSE_EVT: + case ESP_GATTS_LISTEN_EVT: + case ESP_GATTS_CONGEST_EVT: + default: + break; + } +} + +static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + /* If event is register event, store the gatts_if for each profile */ + if (event == ESP_GATTS_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gl_profile_tab[param->reg.app_id].gatts_if = gatts_if; + } else { + ESP_LOGI(GATTS_TAG, "Reg app failed, app_id %04x, status %d\n", + param->reg.app_id, + param->reg.status); + return; + } + } + + /* If the gatts_if equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do { + int idx; + for (idx = 0; idx < PROFILE_NUM; idx++) { + if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gatts_if == gl_profile_tab[idx].gatts_if) { + if (gl_profile_tab[idx].gatts_cb) { + gl_profile_tab[idx].gatts_cb(event, gatts_if, param); + } + } + } + } while (0); +} + void app_main() { esp_err_t ret; - bt_controller_init(); - LOG_INFO("%s init bluetooth\n", __func__); - ret = esp_init_bluetooth(); + esp_bt_controller_init(); + + ret = esp_bluedroid_init(); if (ret) { - LOG_ERROR("%s init bluetooth failed\n", __func__); + ESP_LOGE(GATTS_TAG, "%s init bluetooth failed\n", __func__); return; } - ret = esp_enable_bluetooth(); + ret = esp_bluedroid_enable(); if (ret) { - LOG_ERROR("%s enable bluetooth failed\n", __func__); + ESP_LOGE(GATTS_TAG, "%s enable bluetooth failed\n", __func__); return; } esp_ble_gatts_register_callback(gatts_event_handler); esp_ble_gap_register_callback(gap_event_handler); - esp_ble_gatts_app_register(GATTS_SERVICE_UUID_TEST); + esp_ble_gatts_app_register(PROFILE_A_APP_ID); + esp_ble_gatts_app_register(PROFILE_B_APP_ID); return; } diff --git a/examples/14_gatt_server/sdkconfig.defaults b/examples/14_gatt_server/sdkconfig.defaults index e435f383c8..9d51df5ee5 100644 --- a/examples/14_gatt_server/sdkconfig.defaults +++ b/examples/14_gatt_server/sdkconfig.defaults @@ -1,14 +1,4 @@ # Override some defaults so BT stack is enabled -# in this example - -# -# BT config -# +# and WiFi disabled by default in this example CONFIG_BT_ENABLED=y - -# -# ESP32-specific config -# -CONFIG_ESP32_ENABLE_STACK_BT=y -# CONFIG_ESP32_ENABLE_STACK_NONE is not set -CONFIG_MEMMAP_BT=y +CONFIG_WIFI_ENABLED=n diff --git a/examples/15_gatt_client/Makefile b/examples/15_gatt_client/Makefile index 93f793308f..700ffd7add 100644 --- a/examples/15_gatt_client/Makefile +++ b/examples/15_gatt_client/Makefile @@ -8,11 +8,3 @@ PROJECT_NAME := gatt_client_demo COMPONENT_ADD_INCLUDEDIRS := components/include include $(IDF_PATH)/make/project.mk - -# Copy some defaults into the sdkconfig by default -# so BT stack is enabled -sdkconfig: sdkconfig.defaults - $(Q) cp $< $@ - -menuconfig: sdkconfig -defconfig: sdkconfig diff --git a/examples/15_gatt_client/main/gattc_demo.c b/examples/15_gatt_client/main/gattc_demo.c index 3dc6af6d75..e6ee867293 100644 --- a/examples/15_gatt_client/main/gattc_demo.c +++ b/examples/15_gatt_client/main/gattc_demo.c @@ -37,16 +37,38 @@ #include "esp_gatt_defs.h" #include "esp_bt_main.h" +#define GATTC_TAG "GATTC_DEMO" +///Declare static functions +static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); + + +static esp_gatt_srvc_id_t alert_service_id = { + .id = { + .uuid = { + .len = ESP_UUID_LEN_16, + .uuid = {.uuid16 = 0x1811,}, + }, + .inst_id = 0, + }, + .is_primary = true, +}; + +static esp_gatt_id_t notify_descr_id = { + .uuid = { + .len = ESP_UUID_LEN_16, + .uuid = {.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG,}, + }, + .inst_id = 0, +}; #define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] -esp_gatt_if_t client_if; -esp_gatt_status_t status = ESP_GATT_ERROR; -bool connet = false; -uint16_t simpleClient_id = 0xEE; - -const char device_name[] = "Heart Rate"; +static bool connect = false; +static const char device_name[] = "Alert Notification"; static esp_ble_scan_params_t ble_scan_params = { .scan_type = BLE_SCAN_TYPE_ACTIVE, @@ -57,11 +79,212 @@ static esp_ble_scan_params_t ble_scan_params = { }; -static void esp_gap_cb(uint32_t event, void *param); +#define PROFILE_NUM 2 +#define PROFILE_A_APP_ID 0 +#define PROFILE_B_APP_ID 1 -static void esp_gattc_cb(uint32_t event, void *param); +struct gattc_profile_inst { + esp_gattc_cb_t gattc_cb; + uint16_t gattc_if; + uint16_t app_id; + uint16_t conn_id; + esp_bd_addr_t remote_bda; +}; -static void esp_gap_cb(uint32_t event, void *param) +/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */ +static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = { + [PROFILE_A_APP_ID] = { + .gattc_cb = gattc_profile_a_event_handler, + .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, + [PROFILE_B_APP_ID] = { + .gattc_cb = gattc_profile_b_event_handler, + .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + uint16_t conn_id = 0; + esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; + + switch (event) { + case ESP_GATTC_REG_EVT: + ESP_LOGI(GATTC_TAG, "REG_EVT\n"); + esp_ble_gap_set_scan_params(&ble_scan_params); + break; + case ESP_GATTC_OPEN_EVT: + conn_id = p_data->open.conn_id; + + memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->open.remote_bda, sizeof(esp_bd_addr_t)); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d\n", conn_id, gattc_if, p_data->open.status, p_data->open.mtu); + + ESP_LOGI(GATTC_TAG, "REMOTE BDA %02x:%02x:%02x:%02x:%02x:%02x\n", + gl_profile_tab[PROFILE_A_APP_ID].remote_bda[0], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[1], + gl_profile_tab[PROFILE_A_APP_ID].remote_bda[2], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[3], + gl_profile_tab[PROFILE_A_APP_ID].remote_bda[4], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[5] + ); + + esp_ble_gattc_search_service(gattc_if, conn_id, NULL); + break; + case ESP_GATTC_SEARCH_RES_EVT: { + esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id; + conn_id = p_data->search_res.conn_id; + ESP_LOGI(GATTC_TAG, "SEARCH RES: conn_id = %x\n", conn_id); + if (srvc_id->id.uuid.len == ESP_UUID_LEN_16) { + ESP_LOGI(GATTC_TAG, "UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); + } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_32) { + ESP_LOGI(GATTC_TAG, "UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); + } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_128) { + ESP_LOGI(GATTC_TAG, "UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], + srvc_id->id.uuid.uuid.uuid128[1], srvc_id->id.uuid.uuid.uuid128[2], srvc_id->id.uuid.uuid.uuid128[3], + srvc_id->id.uuid.uuid.uuid128[4], srvc_id->id.uuid.uuid.uuid128[5], srvc_id->id.uuid.uuid.uuid128[6], + srvc_id->id.uuid.uuid.uuid128[7], srvc_id->id.uuid.uuid.uuid128[8], srvc_id->id.uuid.uuid.uuid128[9], + srvc_id->id.uuid.uuid.uuid128[10], srvc_id->id.uuid.uuid.uuid128[11], srvc_id->id.uuid.uuid.uuid128[12], + srvc_id->id.uuid.uuid.uuid128[13], srvc_id->id.uuid.uuid.uuid128[14], srvc_id->id.uuid.uuid.uuid128[15]); + } else { + ESP_LOGE(GATTC_TAG, "UNKNOWN LEN %d\n", srvc_id->id.uuid.len); + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: + conn_id = p_data->search_cmpl.conn_id; + ESP_LOGI(GATTC_TAG, "SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); + esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, NULL); + break; + case ESP_GATTC_GET_CHAR_EVT: + if (p_data->get_char.status != ESP_GATT_OK) { + break; + } + ESP_LOGI(GATTC_TAG, "GET CHAR: conn_id = %x, status %d\n", p_data->get_char.conn_id, p_data->get_char.status); + ESP_LOGI(GATTC_TAG, "GET CHAR: srvc_id = %04x, char_id = %04x\n", p_data->get_char.srvc_id.id.uuid.uuid.uuid16, p_data->get_char.char_id.uuid.uuid.uuid16); + + if (p_data->get_char.char_id.uuid.uuid.uuid16 == 0x2a46) { + ESP_LOGI(GATTC_TAG, "register notify\n"); + esp_ble_gattc_register_for_notify(gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, &alert_service_id, &p_data->get_char.char_id); + } + + esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, &p_data->get_char.char_id); + break; + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + uint16_t notify_en = 1; + ESP_LOGI(GATTC_TAG, "REG FOR NOTIFY: status %d\n", p_data->reg_for_notify.status); + ESP_LOGI(GATTC_TAG, "REG FOR_NOTIFY: srvc_id = %04x, char_id = %04x\n", p_data->reg_for_notify.srvc_id.id.uuid.uuid.uuid16, p_data->reg_for_notify.char_id.uuid.uuid.uuid16); + + esp_ble_gattc_write_char_descr( + gattc_if, + conn_id, + &alert_service_id, + &p_data->reg_for_notify.char_id, + ¬ify_descr_id, + sizeof(notify_en), + (uint8_t *)¬ify_en, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); + break; + } + case ESP_GATTC_NOTIFY_EVT: + ESP_LOGI(GATTC_TAG, "NOTIFY: len %d, value %08x\n", p_data->notify.value_len, *(uint32_t *)p_data->notify.value); + break; + case ESP_GATTC_WRITE_DESCR_EVT: + ESP_LOGI(GATTC_TAG, "WRITE: status %d\n", p_data->write.status); + break; + default: + break; + } +} + +static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + uint16_t conn_id = 0; + esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; + + switch (event) { + case ESP_GATTC_REG_EVT: + ESP_LOGI(GATTC_TAG, "REG_EVT\n"); + break; + case ESP_GATTC_OPEN_EVT: + conn_id = p_data->open.conn_id; + + memcpy(gl_profile_tab[PROFILE_B_APP_ID].remote_bda, p_data->open.remote_bda, sizeof(esp_bd_addr_t)); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d\n", conn_id, gattc_if, p_data->open.status, p_data->open.mtu); + + ESP_LOGI(GATTC_TAG, "REMOTE BDA %02x:%02x:%02x:%02x:%02x:%02x\n", + gl_profile_tab[PROFILE_B_APP_ID].remote_bda[0], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[1], + gl_profile_tab[PROFILE_B_APP_ID].remote_bda[2], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[3], + gl_profile_tab[PROFILE_B_APP_ID].remote_bda[4], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[5] + ); + + esp_ble_gattc_search_service(gattc_if, conn_id, NULL); + break; + case ESP_GATTC_SEARCH_RES_EVT: { + esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id; + conn_id = p_data->search_res.conn_id; + ESP_LOGI(GATTC_TAG, "SEARCH RES: conn_id = %x\n", conn_id); + if (srvc_id->id.uuid.len == ESP_UUID_LEN_16) { + ESP_LOGI(GATTC_TAG, "UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); + } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_32) { + ESP_LOGI(GATTC_TAG, "UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); + } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_128) { + ESP_LOGI(GATTC_TAG, "UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], + srvc_id->id.uuid.uuid.uuid128[1], srvc_id->id.uuid.uuid.uuid128[2], srvc_id->id.uuid.uuid.uuid128[3], + srvc_id->id.uuid.uuid.uuid128[4], srvc_id->id.uuid.uuid.uuid128[5], srvc_id->id.uuid.uuid.uuid128[6], + srvc_id->id.uuid.uuid.uuid128[7], srvc_id->id.uuid.uuid.uuid128[8], srvc_id->id.uuid.uuid.uuid128[9], + srvc_id->id.uuid.uuid.uuid128[10], srvc_id->id.uuid.uuid.uuid128[11], srvc_id->id.uuid.uuid.uuid128[12], + srvc_id->id.uuid.uuid.uuid128[13], srvc_id->id.uuid.uuid.uuid128[14], srvc_id->id.uuid.uuid.uuid128[15]); + } else { + ESP_LOGE(GATTC_TAG, "UNKNOWN LEN %d\n", srvc_id->id.uuid.len); + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: + conn_id = p_data->search_cmpl.conn_id; + ESP_LOGI(GATTC_TAG, "SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); + esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, NULL); + break; + case ESP_GATTC_GET_CHAR_EVT: + if (p_data->get_char.status != ESP_GATT_OK) { + break; + } + ESP_LOGI(GATTC_TAG, "GET CHAR: conn_id = %x, status %d\n", p_data->get_char.conn_id, p_data->get_char.status); + ESP_LOGI(GATTC_TAG, "GET CHAR: srvc_id = %04x, char_id = %04x\n", p_data->get_char.srvc_id.id.uuid.uuid.uuid16, p_data->get_char.char_id.uuid.uuid.uuid16); + + if (p_data->get_char.char_id.uuid.uuid.uuid16 == 0x2a46) { + ESP_LOGI(GATTC_TAG, "register notify\n"); + esp_ble_gattc_register_for_notify(gattc_if, gl_profile_tab[PROFILE_B_APP_ID].remote_bda, &alert_service_id, &p_data->get_char.char_id); + } + + esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, &p_data->get_char.char_id); + break; + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + uint16_t notify_en = 1; + ESP_LOGI(GATTC_TAG, "REG FOR NOTIFY: status %d\n", p_data->reg_for_notify.status); + ESP_LOGI(GATTC_TAG, "REG FOR_NOTIFY: srvc_id = %04x, char_id = %04x\n", p_data->reg_for_notify.srvc_id.id.uuid.uuid.uuid16, p_data->reg_for_notify.char_id.uuid.uuid.uuid16); + + esp_ble_gattc_write_char_descr( + gattc_if, + conn_id, + &alert_service_id, + &p_data->reg_for_notify.char_id, + ¬ify_descr_id, + sizeof(notify_en), + (uint8_t *)¬ify_en, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); + break; + } + case ESP_GATTC_NOTIFY_EVT: + ESP_LOGI(GATTC_TAG, "NOTIFY: len %d, value %08x\n", p_data->notify.value_len, *(uint32_t *)p_data->notify.value); + break; + case ESP_GATTC_WRITE_DESCR_EVT: + ESP_LOGI(GATTC_TAG, "WRITE: status %d\n", p_data->write.status); + break; + default: + break; + } +} + +static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { uint8_t *adv_name = NULL; uint8_t adv_name_len = 0; @@ -77,29 +300,25 @@ static void esp_gap_cb(uint32_t event, void *param) switch (scan_result->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: for (int i = 0; i < 6; i++) { - LOG_INFO("%x:", scan_result->scan_rst.bda[i]); + ESP_LOGI(GATTC_TAG, "%x:", scan_result->scan_rst.bda[i]); } - LOG_INFO("\n"); + ESP_LOGI(GATTC_TAG, "\n"); adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); - LOG_INFO("adv_name_len=%x\n", adv_name_len); + ESP_LOGI(GATTC_TAG, "Searched Device Name Len %d\n", adv_name_len); for (int j = 0; j < adv_name_len; j++) { - LOG_INFO("%c", adv_name[j]); + ESP_LOGI(GATTC_TAG, "%c", adv_name[j]); } - LOG_INFO("\n"); - for (int j = 0; j < adv_name_len; j++) { - LOG_INFO("%c", device_name[j]); - } - LOG_INFO("\n"); if (adv_name != NULL) { if (strcmp((char *)adv_name, device_name) == 0) { - LOG_INFO("the name eque to Heart Rate.\n"); - if (status == ESP_GATT_OK && connet == false) { - connet = true; - LOG_INFO("Connet to the remote device.\n"); + ESP_LOGI(GATTC_TAG, "Searched device %s\n", device_name); + if (connect == false) { + connect = true; + ESP_LOGI(GATTC_TAG, "Connect to the remote device.\n"); esp_ble_gap_stop_scanning(); - esp_ble_gattc_open(client_if, scan_result->scan_rst.bda, true); + esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, scan_result->scan_rst.bda, true); + esp_ble_gattc_open(gl_profile_tab[PROFILE_B_APP_ID].gattc_if, scan_result->scan_rst.bda, true); } } } @@ -116,82 +335,68 @@ static void esp_gap_cb(uint32_t event, void *param) } } - -static void esp_gattc_cb(uint32_t event, void *param) +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { - uint16_t conn_id = 0; - esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; + ESP_LOGI(GATTC_TAG, "EVT %d, gattc if %d\n", event, gattc_if); - LOG_INFO("esp_gattc_cb, event = %x\n", event); - switch (event) { - case ESP_GATTC_REG_EVT: - status = p_data->reg.status; - client_if = p_data->reg.gatt_if; - LOG_INFO("status = %x, client_if = %x\n", status, client_if); - break; - case ESP_GATTC_OPEN_EVT: - conn_id = p_data->open.conn_id; - LOG_INFO("ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d\n", conn_id, p_data->open.gatt_if, p_data->open.status); - esp_ble_gattc_search_service(conn_id, NULL); - break; - case ESP_GATTC_SEARCH_RES_EVT: { - esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id; - conn_id = p_data->open.conn_id; - LOG_INFO("SEARCH RES: conn_id = %x\n", conn_id); - if (srvc_id->id.uuid.len == ESP_UUID_LEN_16) { - LOG_INFO("UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); - } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_32) { - LOG_INFO("UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); - } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_128) { - LOG_INFO("UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], - srvc_id->id.uuid.uuid.uuid128[1], srvc_id->id.uuid.uuid.uuid128[2], srvc_id->id.uuid.uuid.uuid128[3], - srvc_id->id.uuid.uuid.uuid128[4], srvc_id->id.uuid.uuid.uuid128[5], srvc_id->id.uuid.uuid.uuid128[6], - srvc_id->id.uuid.uuid.uuid128[7], srvc_id->id.uuid.uuid.uuid128[8], srvc_id->id.uuid.uuid.uuid128[9], - srvc_id->id.uuid.uuid.uuid128[10], srvc_id->id.uuid.uuid.uuid128[11], srvc_id->id.uuid.uuid.uuid128[12], - srvc_id->id.uuid.uuid.uuid128[13], srvc_id->id.uuid.uuid.uuid128[14], srvc_id->id.uuid.uuid.uuid128[15]); + /* If event is register event, store the gattc_if for each profile */ + if (event == ESP_GATTC_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gl_profile_tab[param->reg.app_id].gattc_if = gattc_if; } else { - LOG_ERROR("UNKNOWN LEN %d\n", srvc_id->id.uuid.len); + ESP_LOGI(GATTC_TAG, "Reg app failed, app_id %04x, status %d\n", + param->reg.app_id, + param->reg.status); + return; } - break; - } - case ESP_GATTC_SEARCH_CMPL_EVT: - conn_id = p_data->search_cmpl.conn_id; - LOG_INFO("SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); - break; - default: - break; } + + /* If the gattc_if equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do { + int idx; + for (idx = 0; idx < PROFILE_NUM; idx++) { + if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gattc_if == gl_profile_tab[idx].gattc_if) { + if (gl_profile_tab[idx].gattc_cb) { + gl_profile_tab[idx].gattc_cb(event, gattc_if, param); + } + } + } + } while (0); } void ble_client_appRegister(void) { - LOG_INFO("register callback\n"); + esp_err_t status; + + ESP_LOGI(GATTC_TAG, "register callback\n"); //register the scan callback function to the gap moudule if ((status = esp_ble_gap_register_callback(esp_gap_cb)) != ESP_OK) { - LOG_ERROR("gap register error, error code = %x\n", status); + ESP_LOGE(GATTC_TAG, "gap register error, error code = %x\n", status); return; } //register the callback function to the gattc module if ((status = esp_ble_gattc_register_callback(esp_gattc_cb)) != ESP_OK) { - LOG_ERROR("gattc register error, error code = %x\n", status); + ESP_LOGE(GATTC_TAG, "gattc register error, error code = %x\n", status); return; } - esp_ble_gattc_app_register(simpleClient_id); - esp_ble_gap_set_scan_params(&ble_scan_params); + esp_ble_gattc_app_register(PROFILE_A_APP_ID); + esp_ble_gattc_app_register(PROFILE_B_APP_ID); } void gattc_client_test(void) { - esp_init_bluetooth(); - esp_enable_bluetooth(); + esp_bluedroid_init(); + esp_bluedroid_enable(); ble_client_appRegister(); } void app_main() { - bt_controller_init(); + esp_bt_controller_init(); gattc_client_test(); } diff --git a/examples/15_gatt_client/sdkconfig.defaults b/examples/15_gatt_client/sdkconfig.defaults index e435f383c8..9d51df5ee5 100644 --- a/examples/15_gatt_client/sdkconfig.defaults +++ b/examples/15_gatt_client/sdkconfig.defaults @@ -1,14 +1,4 @@ # Override some defaults so BT stack is enabled -# in this example - -# -# BT config -# +# and WiFi disabled by default in this example CONFIG_BT_ENABLED=y - -# -# ESP32-specific config -# -CONFIG_ESP32_ENABLE_STACK_BT=y -# CONFIG_ESP32_ENABLE_STACK_NONE is not set -CONFIG_MEMMAP_BT=y +CONFIG_WIFI_ENABLED=n diff --git a/examples/16_pcnt/README.md b/examples/16_pcnt/README.md new file mode 100644 index 0000000000..a7019ea9e7 --- /dev/null +++ b/examples/16_pcnt/README.md @@ -0,0 +1,14 @@ +# Example: pcnt + +This example uses the pulse counter module (PCNT) to count the rising edges of pulses generated by the LED Controller module (LEDC). + +By default GPIO18 is used as output pin, GPIO4 is used as pulse input pin and GPIO5 is used as control input pin. This configuration (pin numbers, etc.) can be modified in top of the main/pcnt_test.c file. + +* Open serial port to view the message printed on your screen +* To do this test, you should connect GPIO18 with GPIO4 +* GPIO5 is the control signal, you can leave it floating with internal pulled up, or connect it to ground. HIGH = Count increases, LOW = count decreases. +* An interrupt is configured to trigger when the count reaches threshold values. +* The counter will reset when it reaches the limit values. + + + diff --git a/examples/16_pcnt/main/pcnt_test.c b/examples/16_pcnt/main/pcnt_test.c index b8489ecb2f..30e6559400 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -36,14 +36,14 @@ * When counter value reaches thresh1 or thresh0 value, it will trigger interrupt. * When counter value reaches l_lim value or h_lim value, counter value will be reset to zero and trigger interrupt. */ -#define PCNT_TEST_UNIT PCNT_UNIT_0 -#define PCNT_H_LIM_VAL (10) -#define PCNT_L_LIM_VAL (-10) -#define PCNT_THRESH1_VAL (5) -#define PCNT_THRESH0_VAL (-5) -#define PCNT_INPUT_SIG_IO (4) -#define PCNT_INPUT_CTRL_IO (5) -#define LEDC_OUPUT_IO (18) +#define PCNT_TEST_UNIT PCNT_UNIT_0 +#define PCNT_H_LIM_VAL 10 +#define PCNT_L_LIM_VAL -10 +#define PCNT_THRESH1_VAL 5 +#define PCNT_THRESH0_VAL -5 +#define PCNT_INPUT_SIG_IO 4 /* Pulse Input GPIO */ +#define PCNT_INPUT_CTRL_IO 5 /* Control GPIO HIGH=count up, LOW=count down */ +#define LEDC_OUTPUT_IO 18 /* Output GPIO */ xQueueHandle pcnt_evt_queue; /*A queue to handle pulse counter event*/ @@ -96,8 +96,8 @@ void IRAM_ATTR pcnt_intr_handler(void* arg) static void ledc_init(void) { ledc_channel_config_t ledc_channel; - /*use GPIO18 as output pin*/ - ledc_channel.gpio_num = LEDC_OUPUT_IO; + /*use LEDC_OUTPUT_IO as output pin*/ + ledc_channel.gpio_num = LEDC_OUTPUT_IO; /*LEDC high speed mode */ ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; /*use LEDC channel 1*/ @@ -125,9 +125,9 @@ static void ledc_init(void) static void pcnt_init(void) { pcnt_config_t pcnt_config = { - /*Set GPIO4 as pulse input gpio */ + /*Set PCNT_INPUT_SIG_IO as pulse input gpio */ .pulse_gpio_num = PCNT_INPUT_SIG_IO, - /*set gpio5 as control gpio */ + /*set PCNT_INPUT_CTRL_IO as control gpio */ .ctrl_gpio_num = PCNT_INPUT_CTRL_IO, /*Choose channel 0 */ .channel = PCNT_CHANNEL_0, @@ -196,7 +196,7 @@ void app_main() portBASE_TYPE res; while(1) { - res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_RATE_MS); + res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_PERIOD_MS); if(res == pdTRUE) { pcnt_get_counter_value(PCNT_TEST_UNIT, &count); printf("Event PCNT unit[%d]; cnt: %d\n", evt.unit, count); diff --git a/examples/17_ethernet/main/ethernet_main.c b/examples/17_ethernet/main/ethernet_main.c index 7e84a9badd..57c6f97357 100644 --- a/examples/17_ethernet/main/ethernet_main.c +++ b/examples/17_ethernet/main/ethernet_main.c @@ -71,6 +71,22 @@ bool phy_tlk110_check_phy_link_status(void) return ((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & LINK_STATUS) == LINK_STATUS ); } +bool phy_tlk110_get_partner_pause_enable(void) +{ + if((esp_eth_smi_read(PHY_LINK_PARTNER_ABILITY_REG) & PARTNER_PAUSE) == PARTNER_PAUSE) { + return true; + } else { + return false; + } +} + +void phy_enable_flow_ctrl(void) +{ + uint32_t data = 0; + data = esp_eth_smi_read(AUTO_NEG_ADVERTISEMENT_REG); + esp_eth_smi_write(AUTO_NEG_ADVERTISEMENT_REG,data|ASM_DIR|PAUSE); +} + void phy_tlk110_init(void) { esp_eth_smi_write(PHY_RESET_CONTROL_REG, SOFTWARE_RESET); @@ -78,8 +94,12 @@ void phy_tlk110_init(void) while (esp_eth_smi_read(PHY_IDENTIFIER_REG) != OUI_MSB_21TO6_DEF) { } - esp_eth_smi_write(SOFTWARE_STAP_CONTROL_REG, DEFAULT_PHY_CONFIG |SW_STRAP_CONFIG_DONE); + esp_eth_smi_write(SOFTWARE_STRAP_CONTROL_REG, DEFAULT_PHY_CONFIG |SW_STRAP_CONFIG_DONE); + ets_delay_us(300); + + //if config.flow_ctrl_enable == true ,enable this + phy_enable_flow_ctrl(); } void eth_gpio_config_rmii(void) @@ -108,11 +128,11 @@ void eth_task(void *pvParameter) { tcpip_adapter_ip_info_t ip; memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); while (1) { - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); if (tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip) == 0) { ESP_LOGI(TAG, "\n~~~~~~~~~~~\n"); @@ -140,6 +160,9 @@ void app_main() config.phy_check_link = phy_tlk110_check_phy_link_status; config.phy_get_speed_mode = phy_tlk110_get_speed_mode; config.phy_get_duplex_mode = phy_tlk110_get_duplex_mode; + //Only FULLDUPLEX mode support flow ctrl now! + config.flow_ctrl_enable = true; + config.phy_get_partner_pause_enable = phy_tlk110_get_partner_pause_enable; ret = esp_eth_init(&config); diff --git a/examples/14_ethernet/main/tlk110_phy.h b/examples/17_ethernet/main/tlk110_phy.h similarity index 74% rename from examples/14_ethernet/main/tlk110_phy.h rename to examples/17_ethernet/main/tlk110_phy.h index 5f2ca644dc..a21bc57aea 100644 --- a/examples/14_ethernet/main/tlk110_phy.h +++ b/examples/17_ethernet/main/tlk110_phy.h @@ -5,7 +5,15 @@ #define PHY_IDENTIFIER_REG (0x2) #define OUI_MSB_21TO6_DEF 0x2000 -#define SOFTWARE_STAP_CONTROL_REG (0x9) +#define AUTO_NEG_ADVERTISEMENT_REG (0x4) +#define ASM_DIR BIT(11) +#define PAUSE BIT(10) + +#define PHY_LINK_PARTNER_ABILITY_REG (0x5) +#define PARTNER_ASM_DIR BIT(11) +#define PARTNER_PAUSE BIT(10) + +#define SOFTWARE_STRAP_CONTROL_REG (0x9) #define SW_STRAP_CONFIG_DONE BIT(15) #define AUTO_MDIX_ENABLE BIT(14) #define AUTO_NEGOTIATION_ENABLE BIT(13) diff --git a/examples/18_i2c/Makefile b/examples/18_i2c/Makefile new file mode 100644 index 0000000000..d575e29b9d --- /dev/null +++ b/examples/18_i2c/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := i2c + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/18_i2c/README.md b/examples/18_i2c/README.md new file mode 100644 index 0000000000..7a88894bf1 --- /dev/null +++ b/examples/18_i2c/README.md @@ -0,0 +1,29 @@ +# I2C Example + + +* This example will show you how to use I2C module by running two tasks on i2c bus: + + * read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance. + * Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip. + +* Pin assignment: + + * slave : + * GPIO25 is assigned as the data signal of i2c slave port + * GPIO26 is assigned as the clock signal of i2c slave port + * master: + * GPIO18 is assigned as the data signal of i2c master port + * GPIO19 is assigned as the clock signal of i2c master port + +* Connection: + + * connect GPIO18 with GPIO25 + * connect GPIO19 with GPIO26 + * connect sda/scl of sensor with GPIO18/GPIO19 + * no need to add external pull-up resistors, driver will enable internal pull-up resistors. + +* Test items: + + * read the sensor data, if connected. + * i2c master(ESP32) will write data to i2c slave(ESP32). + * i2c master(ESP32) will read data from i2c slave(ESP32). diff --git a/examples/18_i2c/main/component.mk b/examples/18_i2c/main/component.mk new file mode 100644 index 0000000000..44bd2b5273 --- /dev/null +++ b/examples/18_i2c/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/18_i2c/main/i2c_test.c b/examples/18_i2c/main/i2c_test.c new file mode 100644 index 0000000000..2ef6d75b2f --- /dev/null +++ b/examples/18_i2c/main/i2c_test.c @@ -0,0 +1,303 @@ +/* i2c - Example + + For other examples please check: + https://github.com/espressif/esp-idf/tree/master/examples + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "driver/i2c.h" + +/** + * TEST CODE BRIEF + * + * This example will show you how to use I2C module by running two tasks on i2c bus: + * + * - read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance. + * - Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip. + * + * Pin assignment: + * + * - slave : + * GPIO25 is assigned as the data signal of i2c slave port + * GPIO26 is assigned as the clock signal of i2c slave port + * - master: + * GPIO18 is assigned as the data signal of i2c master port + * GPIO19 is assigned as the clock signal of i2c master port + * + * Connection: + * + * - connect GPIO18 with GPIO25 + * - connect GPIO19 with GPIO26 + * - connect sda/scl of sensor with GPIO18/GPIO19 + * - no need to add external pull-up resistors, driver will enable internal pull-up resistors. + * + * Test items: + * + * - read the sensor data, if connected. + * - i2c master(ESP32) will write data to i2c slave(ESP32). + * - i2c master(ESP32) will read data from i2c slave(ESP32). + */ + +#define DATA_LENGTH 512 /*! 1) { + i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); + } + i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief Test code to write esp-i2c-slave + * Master device write data to slave(both esp32), + * the data will be stored in slave buffer. + * We can read them out from slave buffer. + * + * ___________________________________________________________________ + * | start | slave_addr + wr_bit + ack | write n bytes + ack | stop | + * --------|---------------------------|----------------------|------| + * + */ +esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t size) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief test code to write esp-i2c-slave + * + * 1. set mode + * _________________________________________________________________ + * | start | slave_addr + wr_bit + ack | write 1 byte + ack | stop | + * --------|---------------------------|---------------------|------| + * 2. wait more than 24 ms + * 3. read data + * ______________________________________________________________________________________ + * | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop | + * --------|---------------------------|--------------------|--------------------|------| + */ +esp_err_t i2c_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, BH1750_CMD_START, ACK_CHECK_EN); + i2c_master_stop(cmd); + int ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret == ESP_FAIL) { + return ret; + } + vTaskDelay(30 / portTICK_RATE_MS); + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_EN); + i2c_master_read_byte(cmd, data_h, ACK_VAL); + i2c_master_read_byte(cmd, data_l, NACK_VAL); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret == ESP_FAIL) { + return ESP_FAIL; + } + return ESP_OK; +} + +/** + * @brief i2c master initialization + */ +void i2c_master_init() +{ + int i2c_master_port = I2C_MASTER_NUM; + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = I2C_MASTER_SDA_IO; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_io_num = I2C_MASTER_SCL_IO; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = I2C_MASTER_FREQ_HZ; + i2c_param_config(i2c_master_port, &conf); + i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); +} + +/** + * @brief i2c slave initialization + */ +void i2c_slave_init() +{ + int i2c_slave_port = I2C_SLAVE_NUM; + i2c_config_t conf_slave; + conf_slave.sda_io_num = I2C_SLAVE_SDA_IO; + conf_slave.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf_slave.scl_io_num = I2C_SLAVE_SCL_IO; + conf_slave.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf_slave.mode = I2C_MODE_SLAVE; + conf_slave.slave.addr_10bit_en = 0; + conf_slave.slave.slave_addr = ESP_SLAVE_ADDR; + i2c_param_config(i2c_slave_port, &conf_slave); + i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0); +} + +/** + * @brief test function to show buffer + */ +void disp_buf(uint8_t* buf, int len) +{ + int i; + for (i = 0; i < len; i++) { + printf("%02x ", buf[i]); + if (( i + 1 ) % 16 == 0) { + printf("\n"); + } + } + printf("\n"); +} + +void i2c_test_task(void* arg) +{ + int i = 0; + int ret; + uint32_t task_idx = (uint32_t) arg; + uint8_t* data = (uint8_t*) malloc(DATA_LENGTH); + uint8_t* data_wr = (uint8_t*) malloc(DATA_LENGTH); + uint8_t* data_rd = (uint8_t*) malloc(DATA_LENGTH); + uint8_t sensor_data_h, sensor_data_l; + + while (1) { + ret = i2c_master_sensor_test( I2C_MASTER_NUM, &sensor_data_h, &sensor_data_l); + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER READ SENSOR( BH1750 )\n", task_idx); + printf("*******************\n"); + if (ret == ESP_OK) { + printf("data_h: %02x\n", sensor_data_h); + printf("data_l: %02x\n", sensor_data_l); + printf("sensor val: %f\n", ( sensor_data_h << 8 | sensor_data_l ) / 1.2); + } else { + printf("No ack, sensor not connected...skip...\n"); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + + //--------------------------------------------------- + for (i = 0; i < DATA_LENGTH; i++) { + data[i] = i; + } + size_t d_size = i2c_slave_write_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); + if (d_size == 0) { + printf("i2c slave tx buffer full\n"); + ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, DATA_LENGTH); + } else { + ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, RW_TEST_LENGTH); + } + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER READ FROM SLAVE\n", task_idx); + printf("*******************\n"); + printf("====TASK[%d] Slave buffer data ====\n", task_idx); + disp_buf(data, d_size); + if (ret == ESP_OK) { + printf("====TASK[%d] Master read ====\n", task_idx); + disp_buf(data_rd, d_size); + } else { + printf("Master read slave error, IO not connected...\n"); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + //--------------------------------------------------- + int size; + for (i = 0; i < DATA_LENGTH; i++) { + data_wr[i] = i + 10; + } + //we need to fill the slave buffer so that master can read later + ret = i2c_master_write_slave( I2C_MASTER_NUM, data_wr, RW_TEST_LENGTH); + if (ret == ESP_OK) { + size = i2c_slave_read_buffer( I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); + } + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER WRITE TO SLAVE\n", task_idx); + printf("*******************\n"); + printf("----TASK[%d] Master write ----\n", task_idx); + disp_buf(data_wr, RW_TEST_LENGTH); + if (ret == ESP_OK) { + printf("----TASK[%d] Slave read: [%d] bytes ----\n", task_idx, size); + disp_buf(data, size); + } else { + printf("TASK[%d] Master write slave error, IO not connected....\n", task_idx); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + } +} + +void app_main() +{ + print_mux = xSemaphoreCreateMutex(); + i2c_slave_init(); + i2c_master_init(); + + xTaskCreate(i2c_test_task, "i2c_test_task_0", 1024 * 2, (void* ) 0, 10, NULL); + xTaskCreate(i2c_test_task, "i2c_test_task_1", 1024 * 2, (void* ) 1, 10, NULL); +} + diff --git a/examples/19_sigmadelta/README.md b/examples/19_sigmadelta/README.md new file mode 100644 index 0000000000..e8cac2c87b --- /dev/null +++ b/examples/19_sigmadelta/README.md @@ -0,0 +1,7 @@ +# Example: sigma_delta modulation + +This example uses the sigma_delta output modulation driver to generate modulated output on a GPIO. + +By default the GPIO output is 4, however you can edit this in the `sigmadelta_init()` function inside `main/sigmadelta_test.c`. + +If you connect an LED to the output GPIO, you will see it blinking slowly. diff --git a/examples/19_sigmadelta/main/sigmadelta_test.c b/examples/19_sigmadelta/main/sigmadelta_test.c index 92bc6e0a83..60880311d5 100644 --- a/examples/19_sigmadelta/main/sigmadelta_test.c +++ b/examples/19_sigmadelta/main/sigmadelta_test.c @@ -46,7 +46,7 @@ void app_main() while(1) { sigmadelta_set_duty(SIGMADELTA_CHANNEL_0, duty); /*by changing delay time, you can change the blink frequency of LED. */ - vTaskDelay(10 / portTICK_RATE_MS); + vTaskDelay(10 / portTICK_PERIOD_MS); duty += inc; if(duty == 127 || duty == -127) inc = (-1) * inc; diff --git a/examples/21_gpio/Makefile b/examples/21_gpio/Makefile new file mode 100644 index 0000000000..0589a9f21d --- /dev/null +++ b/examples/21_gpio/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := gpio + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/21_gpio/README.md b/examples/21_gpio/README.md new file mode 100644 index 0000000000..7436d77532 --- /dev/null +++ b/examples/21_gpio/README.md @@ -0,0 +1,20 @@ +# Example: GPIO + +###This test code shows how to configure gpio and how to use gpio interrupt. + + +####GPIO functions: + + * GPIO18: output + * GPIO19: output + * GPIO4: input, pulled up, interrupt from rising edge and falling edge + * GPIO5: input, pulled up, interrupt from rising edge. + +####Test: + * Connect GPIO18 with GPIO4 + * Connect GPIO19 with GPIO5 + * Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5 + + + + diff --git a/examples/21_gpio/main/component.mk b/examples/21_gpio/main/component.mk new file mode 100644 index 0000000000..44bd2b5273 --- /dev/null +++ b/examples/21_gpio/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/21_gpio/main/gpio_test.c b/examples/21_gpio/main/gpio_test.c new file mode 100644 index 0000000000..a5e9571bca --- /dev/null +++ b/examples/21_gpio/main/gpio_test.c @@ -0,0 +1,114 @@ +/* GPIO Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/gpio.h" + +/** + * Brief: + * This test code shows how to configure gpio and how to use gpio interrupt. + * + * GPIO status: + * GPIO18: output + * GPIO19: output + * GPIO4: input, pulled up, interrupt from rising edge and falling edge + * GPIO5: input, pulled up, interrupt from rising edge. + * + * Test: + * Connect GPIO18 with GPIO4 + * Connect GPIO19 with GPIO5 + * Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5 + * + */ + +#define GPIO_OUTPUT_IO_0 18 +#define GPIO_OUTPUT_IO_1 19 +#define GPIO_OUTPUT_PIN_SEL ((1< +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "driver/i2s.h" +#include + + +#define SAMPLE_RATE (36000) +#define I2S_NUM (0) +#define WAVE_FREQ_HZ (100) +#define PI 3.14159265 + +#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ) + +void app_main() +{ + unsigned int i, sample_val; + float sin_float, triangle_float, triangle_step = 65536.0 / SAMPLE_PER_CYCLE; + //for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes each sample) + //using 6 buffers, we need 60-samples per buffer + //2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX + .sample_rate = SAMPLE_RATE, + .bits_per_sample = 16, //16-bit per channel + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, + .dma_buf_count = 6, + .dma_buf_len = 60, // + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1 + }; + i2s_pin_config_t pin_config = { + .bck_io_num = 26, + .ws_io_num = 25, + .data_out_num = 22, + .data_in_num = -1 //Not used + }; + + nvs_flash_init(); + i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); + i2s_set_pin(I2S_NUM, &pin_config); + + triangle_float = -32767; + + for(i = 0; i < SAMPLE_PER_CYCLE; i++) { + sin_float = sin(i * PI / 180.0); + if(sin_float >= 0) + triangle_float += triangle_step; + else + triangle_float -= triangle_step; + sin_float *= 32767; + + sample_val = 0; + sample_val += (short)triangle_float; + sample_val = sample_val << 16; + sample_val += (short) sin_float; + + i2s_push_sample(I2S_NUM, (char *)&sample_val, portMAX_DELAY); + } +} diff --git a/examples/22_i2s/main/component.mk b/examples/22_i2s/main/component.mk new file mode 100644 index 0000000000..dbc5a7a9e7 --- /dev/null +++ b/examples/22_i2s/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the SDK documents if you need to do this. +# + +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/examples/23_coap_client/Makefile b/examples/23_coap_client/Makefile new file mode 100644 index 0000000000..c9a25d117f --- /dev/null +++ b/examples/23_coap_client/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := coap_client + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/23_coap_client/main/Kconfig.projbuild b/examples/23_coap_client/main/Kconfig.projbuild new file mode 100644 index 0000000000..045137a0e9 --- /dev/null +++ b/examples/23_coap_client/main/Kconfig.projbuild @@ -0,0 +1,21 @@ +menu "Example Configuration" + +config TARGET_DOMAIN_URI + string "Target Uri" + default "coap://californium.eclipse.org" + help + Target uri for the example to use. + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/23_coap_client/main/coap_client.c b/examples/23_coap_client/main/coap_client.c new file mode 100644 index 0000000000..7c0df1afec --- /dev/null +++ b/examples/23_coap_client/main/coap_client.c @@ -0,0 +1,205 @@ +/* CoAP client Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" + +#include "nvs_flash.h" + +#include "coap.h" + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define COAP_DEFAULT_TIME_SEC 5 +#define COAP_DEFAULT_TIME_USEC 0 + +/* The examples use uri "coap://californium.eclipse.org" that + you can set via 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define COAP_DEFAULT_DEMO_URI "coap://californium.eclipse.org" +*/ +#define COAP_DEFAULT_DEMO_URI CONFIG_TARGET_DOMAIN_URI + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "CoAP_client"; + +static void message_handler(struct coap_context_t *ctx, const coap_endpoint_t *local_interface, const coap_address_t *remote, + coap_pdu_t *sent, coap_pdu_t *received, + const coap_tid_t id) +{ + unsigned char* data = NULL; + size_t data_len; + if (COAP_RESPONSE_CLASS(received->hdr->code) == 2) { + if (coap_get_data(received, &data_len, &data)) { + printf("Received: %s\n", data); + } + } +} + +static void coap_demo_thread(void *p) +{ + struct hostent *hp; + struct ip4_addr *ip4_addr; + + coap_context_t* ctx = NULL; + coap_address_t dst_addr, src_addr; + static coap_uri_t uri; + fd_set readfds; + struct timeval tv; + int flags, result; + coap_pdu_t* request = NULL; + const char* server_uri = COAP_DEFAULT_DEMO_URI; + uint8_t get_method = 1; + + while (1) { + /* Wait for the callback to set the CONNECTED_BIT in the + event group. + */ + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + ESP_LOGI(TAG, "Connected to AP"); + + if (coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri) == -1) { + ESP_LOGE(TAG, "CoAP server uri error"); + break; + } + + hp = gethostbyname((const char *)uri.host.s); + + if (hp == NULL) { + ESP_LOGE(TAG, "DNS lookup failed"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + continue; + } + + /* Code to print the resolved IP. + + Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */ + ip4_addr = (struct ip4_addr *)hp->h_addr; + ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*ip4_addr)); + + coap_address_init(&src_addr); + src_addr.addr.sin.sin_family = AF_INET; + src_addr.addr.sin.sin_port = htons(0); + src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; + + ctx = coap_new_context(&src_addr); + if (ctx) { + coap_address_init(&dst_addr); + dst_addr.addr.sin.sin_family = AF_INET; + dst_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); + dst_addr.addr.sin.sin_addr.s_addr = ip4_addr->addr; + + request = coap_new_pdu(); + if (request){ + request->hdr->type = COAP_MESSAGE_CON; + request->hdr->id = coap_new_message_id(ctx); + request->hdr->code = get_method; + coap_add_option(request, COAP_OPTION_URI_PATH, uri.path.length, uri.path.s); + + coap_register_response_handler(ctx, message_handler); + coap_send_confirmed(ctx, ctx->endpoint, &dst_addr, request); + + flags = fcntl(ctx->sockfd, F_GETFL, 0); + fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); + + tv.tv_usec = COAP_DEFAULT_TIME_USEC; + tv.tv_sec = COAP_DEFAULT_TIME_SEC; + + for(;;) { + FD_ZERO(&readfds); + FD_CLR( ctx->sockfd, &readfds ); + FD_SET( ctx->sockfd, &readfds ); + result = select( FD_SETSIZE, &readfds, 0, 0, &tv ); + if (result > 0) { + if (FD_ISSET( ctx->sockfd, &readfds )) + coap_read(ctx); + } else if (result < 0) { + break; + } else { + ESP_LOGE(TAG, "select timeout"); + } + } + } + coap_free_context(ctx); + } + } + + vTaskDelete(NULL); +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); + xTaskCreate(coap_demo_thread, "coap", 2048, NULL, 5, NULL); +} diff --git a/examples/23_coap_client/main/component.mk b/examples/23_coap_client/main/component.mk new file mode 100644 index 0000000000..0b9d7585e7 --- /dev/null +++ b/examples/23_coap_client/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/24_coap_server/Makefile b/examples/24_coap_server/Makefile new file mode 100644 index 0000000000..f82e5fc278 --- /dev/null +++ b/examples/24_coap_server/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := coap_server + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/24_coap_server/main/Kconfig.projbuild b/examples/24_coap_server/main/Kconfig.projbuild new file mode 100644 index 0000000000..7a9cb97a0e --- /dev/null +++ b/examples/24_coap_server/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/24_coap_server/main/coap_server.c b/examples/24_coap_server/main/coap_server.c new file mode 100644 index 0000000000..75e3296f79 --- /dev/null +++ b/examples/24_coap_server/main/coap_server.c @@ -0,0 +1,192 @@ +/* CoAP server Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" + +#include "nvs_flash.h" + +#include "coap.h" + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define COAP_DEFAULT_TIME_SEC 5 +#define COAP_DEFAULT_TIME_USEC 0 + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "CoAP_server"; + +static coap_async_state_t *async = NULL; + +static void +send_async_response(coap_context_t *ctx, const coap_endpoint_t *local_if) +{ + coap_pdu_t *response; + unsigned char buf[3]; + const char* response_data = "Hello World!"; + size_t size = sizeof(coap_hdr_t) + 20; + response = coap_pdu_init(async->flags & COAP_MESSAGE_CON, COAP_RESPONSE_CODE(205), 0, size); + response->hdr->id = coap_new_message_id(ctx); + if (async->tokenlen) + coap_add_token(response, async->tokenlen, async->token); + coap_add_option(response, COAP_OPTION_CONTENT_TYPE, coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf); + coap_add_data (response, strlen(response_data), (unsigned char *)response_data); + + if (coap_send(ctx, local_if, &async->peer, response) == COAP_INVALID_TID) { + + } + coap_delete_pdu(response); + coap_async_state_t *tmp; + coap_remove_async(ctx, async->id, &tmp); + coap_free_async(async); + async = NULL; +} + +/* + * The resource handler + */ +static void +async_handler(coap_context_t *ctx, struct coap_resource_t *resource, + const coap_endpoint_t *local_interface, coap_address_t *peer, + coap_pdu_t *request, str *token, coap_pdu_t *response) +{ + async = coap_register_async(ctx, peer, request, COAP_ASYNC_SEPARATE | COAP_ASYNC_CONFIRM, (void*)"no data"); +} + +static void coap_demo_thread(void *p) +{ + coap_context_t* ctx = NULL; + coap_address_t serv_addr; + coap_resource_t* resource = NULL; + fd_set readfds; + struct timeval tv; + int flags = 0; + + while (1) { + /* Wait for the callback to set the CONNECTED_BIT in the + event group. + */ + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + ESP_LOGI(TAG, "Connected to AP"); + + /* Prepare the CoAP server socket */ + coap_address_init(&serv_addr); + serv_addr.addr.sin.sin_family = AF_INET; + serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; + serv_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); + ctx = coap_new_context(&serv_addr); + if (ctx) { + flags = fcntl(ctx->sockfd, F_GETFL, 0); + fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); + + tv.tv_usec = COAP_DEFAULT_TIME_USEC; + tv.tv_sec = COAP_DEFAULT_TIME_SEC; + /* Initialize the resource */ + resource = coap_resource_init((unsigned char *)"Espressif", 9, 0); + if (resource){ + coap_register_handler(resource, COAP_REQUEST_GET, async_handler); + coap_add_resource(ctx, resource); + /*For incoming connections*/ + for (;;) { + FD_ZERO(&readfds); + FD_CLR( ctx->sockfd, &readfds); + FD_SET( ctx->sockfd, &readfds); + + int result = select( FD_SETSIZE, &readfds, 0, 0, &tv ); + if (result > 0){ + if (FD_ISSET( ctx->sockfd, &readfds )) + coap_read(ctx); + } else if (result < 0){ + break; + } else { + ESP_LOGE(TAG, "select timeout"); + } + + if (async) { + send_async_response(ctx, ctx->endpoint); + } + } + } + + coap_free_context(ctx); + } + } + + vTaskDelete(NULL); +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); + + xTaskCreate(coap_demo_thread, "coap", 2048, NULL, 5, NULL); +} diff --git a/examples/24_coap_server/main/component.mk b/examples/24_coap_server/main/component.mk new file mode 100644 index 0000000000..0b9d7585e7 --- /dev/null +++ b/examples/24_coap_server/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/25_ota/Makefile b/examples/25_ota/Makefile new file mode 100644 index 0000000000..7ffb10234f --- /dev/null +++ b/examples/25_ota/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ota + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/25_ota/OTA_workflow.png b/examples/25_ota/OTA_workflow.png new file mode 100644 index 0000000000..3e91dafb38 Binary files /dev/null and b/examples/25_ota/OTA_workflow.png differ diff --git a/examples/25_ota/README.md b/examples/25_ota/README.md new file mode 100644 index 0000000000..49a47d3799 --- /dev/null +++ b/examples/25_ota/README.md @@ -0,0 +1,101 @@ + +# Simple OTA Demo + +This example demonstrates a working OTA (over the air) firmware update workflow. + +This example is a *simplified demonstration*, for production firmware updates you should use a secure protocol such as HTTPS. + +--- + +# Aim + +An app running on ESP32 can upgrade itself by downloading a new app "image" binary file, and storing it in flash. + +In this example, the ESP32 has 3 images in flash: factory, OTA_0, OTA_1. Each of these is a self-contained partition. The number of OTA image partition is determined by the partition table layout. + +Flashing the example over serial with "make flash" updates the factory app image. On first boot, the bootloader loads this factory app image which then performs an OTA update (triggered in the example code). The update downloads a new image from an http server and saves it into the OTA_0 partition. At this point the example code updates the ota_data partition to indicate the new app partition, and resets. The bootloader reads ota_data, determines the new OTA image has been selected, and runs it. + + +# Worflow + +The OTA_workflow.png diagram demonstrates the overall workflow: + +![OTA Workflow diagram](OTA_workflow.png) + +## Step 1: Connect to AP + +Connect your host PC to the same AP that you will use for the ESP32. + +## Step 2: Run HTTP Server + +Python has a built-in HTTP server that can be used for example purposes. + +For our upgrade example OTA file, we're going to use the `01_hello_world` example. + +Open a new terminal to run the HTTP server, then run these commands to build the example and start the server: + +``` +cd $IDF_PATH/examples/01_hello_world +make +cd build +python -m SimpleHTTPServer 8070 +``` + +While the server is running, the contents of the build directory can be browsed at http://localhost:8070/ + +NB: On some systems, the command may be `python2 -m SimpleHTTPServer`. + +NB: You've probably noticed there is nothing special about the "hello world" example when used for OTA updates. This is because any .bin app file which is built by esp-idf can be used as an app image for OTA. The only difference is whether it is written to a factory partition or an OTA partition. + +If you have any firewall software running that will block incoming access to port 8070, configure it to allow access while running the example. + +## Step 3: Build OTA Example + +Change back to the OTA example directory, and type `make menuconfig` to configure the OTA example. Under the "Example Configuration" submenu, fill in the following details: + +* WiFi SSID & Password +* IP address of your host PC as "HTTP Server" +* HTTP Port number (if using the Python HTTP server above, the default is correct) + +If serving the "hello world" example, you can leave the default filename as-is. + +Save your changes, and type `make` to build the example. + +## Step 4: Flash OTA Example + +When flashing, use the `erase_flash` target first to erase the entire flash (this deletes any leftover data in the ota_data partition). Then flash the factory image over serial: + +``` +make erase_flash flash +``` + +(The `make erase_flash flash` means "erase everything, then flash". `make flash` only erases the parts of flash which are being rewritten.) + +## Step 5: Run the OTA Example + +When the example starts up, it will print "ota: Starting OTA example..." then: + +1. Connect to the AP with configured SSID and password. +2. Connect to the HTTP server and download the new image. +3. Write the image to flash, and configure the next boot from this image. +4. Reboot + +# Troubleshooting + +* Check your PC can ping the ESP32 at its IP, and that the IP, AP and other configuration settings are correct in menuconfig. +* Check if any firewall software is preventing incoming connections on the PC. +* Check you can see the configured file (default hello-world.bin) if you browse the file listing at http://127.0.0.1/ +* If you have another PC or a phone, try viewing the file listing from the separate host. + +## Error "ota_begin error err=0x104" + +If you see this error then check that the configured (and actual) flash size is large enough for the partitions in the partition table. The default "two OTA slots" partition table only works with 4MB flash size. To use OTA with smaller flash sizes, create a custom partition table CSV (look in components/partition_table) and configure it in menuconfig. + +If changing partition layout, it is usually wise to run "make erase_flash" between steps. + +## Production Implementation + +If scaling this example for production use, please consider: + +* Using an encrypted communications channel such as HTTPS. +* Dealing with timeouts or WiFi disconnections while flashing. diff --git a/examples/25_ota/main/Kconfig.projbuild b/examples/25_ota/main/Kconfig.projbuild new file mode 100644 index 0000000000..8727d1b76c --- /dev/null +++ b/examples/25_ota/main/Kconfig.projbuild @@ -0,0 +1,40 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "myssid" + help + WiFi password (WPA or WPA2) for the example to use. + + Can be left blank if the network has no security set. + +config SERVER_IP + string "HTTP Server IP" + default "192.168.0.3" + help + HTTP Server IP to download the image file from. + + See example README.md for details. + +config SERVER_PORT + string "HTTP Server Port" + default "8070" + help + HTTP Server port to connect to. + Should be chosen not to conflict with any other port used + on the system. + +config EXAMPLE_FILENAME + string "HTTP GET Filename" + default "/hello-world.bin" + help + Filename of the app image file to download for + the OTA update. + +endmenu diff --git a/examples/25_ota/main/component.mk b/examples/25_ota/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/25_ota/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/25_ota/main/ota.c b/examples/25_ota/main/ota.c new file mode 100644 index 0000000000..4956129e5d --- /dev/null +++ b/examples/25_ota/main/ota.c @@ -0,0 +1,320 @@ +/* OTA example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "esp_ota_ops.h" +#include "esp_partition.h" + +#include "nvs_flash.h" + +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD +#define EXAMPLE_SERVER_IP CONFIG_SERVER_IP +#define EXAMPLE_SERVER_PORT CONFIG_SERVER_PORT +#define EXAMPLE_FILENAME CONFIG_EXAMPLE_FILENAME +#define BUFFSIZE 1024 +#define TEXT_BUFFSIZE 1024 + +static const char *TAG = "ota"; +/*an ota data write buffer ready to write to the flash*/ +char ota_write_data[BUFFSIZE + 1] = { 0 }; +/*an packet receive buffer*/ +char text[BUFFSIZE + 1] = { 0 }; +/* an image total length*/ +int binary_file_length = 0; +/*socket id*/ +int socket_id = -1; +char http_request[64] = {0}; +/* operate handle : uninitialized value is zero ,every ota begin would exponential growth*/ +esp_ota_handle_t out_handle = 0; +esp_partition_t operate_partition; + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +/*read buffer by byte still delim ,return read bytes counts*/ +int read_until(char *buffer, char delim, int len) +{ +// /*TODO: delim check,buffer check,further: do an buffer length limited*/ + int i = 0; + while (buffer[i] != delim && i < len) { + ++i; + } + return i + 1; +} + +/* resolve a packet from http socket + * return true if packet including \r\n\r\n that means http packet header finished,start to receive packet body + * otherwise return false + * */ +bool resolve_pkg(char text[], int total_len, esp_ota_handle_t out_handle) +{ + /* i means current position */ + int i = 0, i_read_len = 0; + while (text[i] != 0 && i < total_len) { + i_read_len = read_until(&text[i], '\n', total_len); + // if we resolve \r\n line,we think packet header is finished + if (i_read_len == 2) { + int i_write_len = total_len - (i + 2); + memset(ota_write_data, 0, BUFFSIZE); + /*copy first http packet body to write buffer*/ + memcpy(ota_write_data, &(text[i + 2]), i_write_len); + /*check write packet header first byte:0xE9 second byte:0x09 */ + if (ota_write_data[0] == 0xE9 && i_write_len >= 2 && ota_write_data[1] == 0x09) { + ESP_LOGI(TAG, "OTA Write Header format Check OK. first byte is %02x ,second byte is %02x", ota_write_data[0], ota_write_data[1]); + } else { + ESP_LOGE(TAG, "OTA Write Header format Check Failed! first byte is %02x ,second byte is %02x", ota_write_data[0], ota_write_data[1]); + return false; + } + + esp_err_t err = esp_ota_write( out_handle, (const void *)ota_write_data, i_write_len); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error: esp_ota_write failed! err=%x", err); + return false; + } else { + ESP_LOGI(TAG, "esp_ota_write header OK"); + binary_file_length += i_write_len; + } + return true; + } + i += i_read_len; + } + return false; +} + +bool connect_to_http_server() +{ + ESP_LOGI(TAG, "Server IP: %s Server Port:%s", EXAMPLE_SERVER_IP, EXAMPLE_SERVER_PORT); + sprintf(http_request, "GET %s HTTP/1.1\r\nHost: %s:%s \r\n\r\n", EXAMPLE_FILENAME, EXAMPLE_SERVER_IP, EXAMPLE_SERVER_PORT); + + int http_connect_flag = -1; + struct sockaddr_in sock_info; + + socket_id = socket(AF_INET, SOCK_STREAM, 0); + if (socket_id == -1) { + ESP_LOGE(TAG, "Create socket failed!"); + return false; + } + + // set connect info + memset(&sock_info, 0, sizeof(struct sockaddr_in)); + sock_info.sin_family = AF_INET; + sock_info.sin_addr.s_addr = inet_addr(EXAMPLE_SERVER_IP); + sock_info.sin_port = htons(atoi(EXAMPLE_SERVER_PORT)); + + // connect to http server + http_connect_flag = connect(socket_id, (struct sockaddr *)&sock_info, sizeof(sock_info)); + if (http_connect_flag == -1) { + ESP_LOGE(TAG, "Connect to server failed! errno=%d", errno); + close(socket_id); + return false; + } else { + ESP_LOGI(TAG, "Connected to server"); + return true; + } + return false; +} + +bool ota_init() +{ + esp_err_t err; + const esp_partition_t *esp_current_partition = esp_ota_get_boot_partition(); + if (esp_current_partition->type != ESP_PARTITION_TYPE_APP) { + ESP_LOGE(TAG, "Error: esp_current_partition->type != ESP_PARTITION_TYPE_APP"); + return false; + } + + esp_partition_t find_partition; + memset(&operate_partition, 0, sizeof(esp_partition_t)); + /*choose which OTA image should we write to*/ + switch (esp_current_partition->subtype) { + case ESP_PARTITION_SUBTYPE_APP_FACTORY: + find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0; + break; + case ESP_PARTITION_SUBTYPE_APP_OTA_0: + find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_1; + break; + case ESP_PARTITION_SUBTYPE_APP_OTA_1: + find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0; + break; + default: + break; + } + find_partition.type = ESP_PARTITION_TYPE_APP; + + const esp_partition_t *partition = esp_partition_find_first(find_partition.type, find_partition.subtype, NULL); + assert(partition != NULL); + memset(&operate_partition, 0, sizeof(esp_partition_t)); + err = esp_ota_begin( partition, OTA_SIZE_UNKNOWN, &out_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_begin failed err=0x%x!", err); + return false; + } else { + memcpy(&operate_partition, partition, sizeof(esp_partition_t)); + ESP_LOGI(TAG, "esp_ota_begin init OK"); + return true; + } + return false; +} + +void __attribute__((noreturn)) task_fatal_error() +{ + ESP_LOGE(TAG, "Exiting task due to fatal error..."); + close(socket_id); + (void)vTaskDelete(NULL); + + while (1) { + ; + } +} + +void main_task(void *pvParameter) +{ + esp_err_t err; + ESP_LOGI(TAG, "Starting OTA example..."); + /* Wait for the callback to set the CONNECTED_BIT in the + event group. + */ + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + ESP_LOGI(TAG, "Connect to Wifi ! Start to Connect to Server...."); + + /*connect to http server*/ + if (connect_to_http_server()) { + ESP_LOGI(TAG, "Connected to http server"); + } else { + ESP_LOGE(TAG, "Connect to http server failed!"); + task_fatal_error(); + } + + int res = -1; + /*send GET request to http server*/ + res = send(socket_id, http_request, strlen(http_request), 0); + if (res == -1) { + ESP_LOGE(TAG, "Send GET request to server failed"); + task_fatal_error(); + } else { + ESP_LOGI(TAG, "Send GET request to server succeeded"); + } + + if ( ota_init() ) { + ESP_LOGI(TAG, "OTA Init succeeded"); + } else { + ESP_LOGE(TAG, "OTA Init failed"); + task_fatal_error(); + } + + bool pkg_body_start = false, flag = true; + /*deal with all receive packet*/ + while (flag) { + memset(text, 0, TEXT_BUFFSIZE); + memset(ota_write_data, 0, BUFFSIZE); + int buff_len = recv(socket_id, text, TEXT_BUFFSIZE, 0); + if (buff_len < 0) { /*receive error*/ + ESP_LOGE(TAG, "Error: receive data error! errno=%d", errno); + task_fatal_error(); + } else if (buff_len > 0 && !pkg_body_start) { /*deal with packet header*/ + memcpy(ota_write_data, text, buff_len); + pkg_body_start = resolve_pkg(text, buff_len, out_handle); + } else if (buff_len > 0 && pkg_body_start) { /*deal with packet body*/ + memcpy(ota_write_data, text, buff_len); + err = esp_ota_write( out_handle, (const void *)ota_write_data, buff_len); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error: esp_ota_write failed! err=%x", err); + task_fatal_error(); + } + binary_file_length += buff_len; + ESP_LOGI(TAG, "Have written image length %d", binary_file_length); + } else if (buff_len == 0) { /*packet over*/ + flag = false; + ESP_LOGI(TAG, "Connection closed, all packets received"); + close(socket_id); + } else { + ESP_LOGE(TAG, "Unexpected recv result"); + } + } + + ESP_LOGI(TAG, "Total Write binary data length : %d", binary_file_length); + + if (esp_ota_end(out_handle) != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_end failed!"); + task_fatal_error(); + } + err = esp_ota_set_boot_partition(&operate_partition); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_set_boot_partition failed! err=0x%x", err); + task_fatal_error(); + } + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); + return ; +} + +void app_main() +{ + nvs_flash_init(); + initialise_wifi(); + xTaskCreate(&main_task, "main_task", 8192, NULL, 5, NULL); +} diff --git a/examples/25_ota/sdkconfig.defaults b/examples/25_ota/sdkconfig.defaults new file mode 100644 index 0000000000..2289a82300 --- /dev/null +++ b/examples/25_ota/sdkconfig.defaults @@ -0,0 +1,4 @@ +# Default sdkconfig parameters to use the OTA +# partition table layout, with a 4MB flash size +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_TWO_OTA=y diff --git a/examples/26_spi_master/Makefile b/examples/26_spi_master/Makefile new file mode 100644 index 0000000000..7ca171bb52 --- /dev/null +++ b/examples/26_spi_master/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := spi_master + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/26_spi_master/main/component.mk b/examples/26_spi_master/main/component.mk new file mode 100644 index 0000000000..4d3b30caf3 --- /dev/null +++ b/examples/26_spi_master/main/component.mk @@ -0,0 +1,5 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/26_spi_master/main/spi_master.c b/examples/26_spi_master/main/spi_master.c new file mode 100644 index 0000000000..577cada4c2 --- /dev/null +++ b/examples/26_spi_master/main/spi_master.c @@ -0,0 +1,275 @@ +/* SPI Master example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "driver/spi_master.h" +#include "soc/gpio_struct.h" +#include "driver/gpio.h" + + +/* + This code displays some fancy graphics on the ILI9341-based 320x240 LCD on an ESP-WROVER_KIT board. + It is not very fast, even when the SPI transfer itself happens at 8MHz and with DMA, because + the rest of the code is not very optimized. Especially calculating the image line-by-line + is inefficient; it would be quicker to send an entire screenful at once. This example does, however, + demonstrate the use of both spi_device_transmit as well as spi_device_queue_trans/spi_device_get_trans_result + as well as pre-transmit callbacks. + + Some info about the ILI9341: It has an C/D line, which is connected to a GPIO here. It expects this + line to be low for a command and high for data. We use a pre-transmit callback here to control that + line: every transaction has as the user-definable argument the needed state of the D/C line and just + before the transaction is sent, the callback will set this line to the correct state. +*/ + +#define PIN_NUM_MISO 25 +#define PIN_NUM_MOSI 23 +#define PIN_NUM_CLK 19 +#define PIN_NUM_CS 22 + +#define PIN_NUM_DC 21 +#define PIN_NUM_RST 18 +#define PIN_NUM_BCKL 5 + + +/* + The ILI9341 needs a bunch of command/argument values to be initialized. They are stored in this struct. +*/ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} ili_init_cmd_t; + +static const ili_init_cmd_t ili_init_cmds[]={ + {0xCF, {0x00, 0x83, 0X30}, 3}, + {0xED, {0x64, 0x03, 0X12, 0X81}, 4}, + {0xE8, {0x85, 0x01, 0x79}, 3}, + {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, + {0xF7, {0x20}, 1}, + {0xEA, {0x00, 0x00}, 2}, + {0xC0, {0x26}, 1}, + {0xC1, {0x11}, 1}, + {0xC5, {0x35, 0x3E}, 2}, + {0xC7, {0xBE}, 1}, + {0x36, {0x28}, 1}, + {0x3A, {0x55}, 1}, + {0xB1, {0x00, 0x1B}, 2}, + {0xF2, {0x08}, 1}, + {0x26, {0x01}, 1}, + {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15}, + {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15}, + {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4}, + {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4}, + {0x2C, {0}, 0}, + {0xB7, {0x07}, 1}, + {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4}, + {0x11, {0}, 0x80}, + {0x29, {0}, 0x80}, + {0, {0}, 0xff}, +}; + +//Send a command to the ILI9341. Uses spi_device_transmit, which waits until the transfer is complete. +void ili_cmd(spi_device_handle_t spi, const uint8_t cmd) +{ + esp_err_t ret; + spi_transaction_t t; + memset(&t, 0, sizeof(t)); //Zero out the transaction + t.length=8; //Command is 8 bits + t.tx_buffer=&cmd; //The data is the cmd itself + t.user=(void*)0; //D/C needs to be set to 0 + ret=spi_device_transmit(spi, &t); //Transmit! + assert(ret==ESP_OK); //Should have had no issues. +} + +//Send data to the ILI9341. Uses spi_device_transmit, which waits until the transfer is complete. +void ili_data(spi_device_handle_t spi, const uint8_t *data, int len) +{ + esp_err_t ret; + spi_transaction_t t; + if (len==0) return; //no need to send anything + memset(&t, 0, sizeof(t)); //Zero out the transaction + t.length=len*8; //Len is in bytes, transaction length is in bits. + t.tx_buffer=data; //Data + t.user=(void*)1; //D/C needs to be set to 1 + ret=spi_device_transmit(spi, &t); //Transmit! + assert(ret==ESP_OK); //Should have had no issues. +} + +//This function is called (in irq context!) just before a transmission starts. It will +//set the D/C line to the value indicated in the user field. +void ili_spi_pre_transfer_callback(spi_transaction_t *t) +{ + int dc=(int)t->user; + gpio_set_level(PIN_NUM_DC, dc); +} + +//Initialize the display +void ili_init(spi_device_handle_t spi) +{ + int cmd=0; + //Initialize non-SPI GPIOs + gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT); + gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT); + gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT); + + //Reset the display + gpio_set_level(PIN_NUM_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(PIN_NUM_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + //Send all the commands + while (ili_init_cmds[cmd].databytes!=0xff) { + ili_cmd(spi, ili_init_cmds[cmd].cmd); + ili_data(spi, ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F); + if (ili_init_cmds[cmd].databytes&0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + + ///Enable backlight + gpio_set_level(PIN_NUM_BCKL, 0); +} + + +//To send a line we have to send a command, 2 data bytes, another command, 2 more data bytes and another command +//before sending the line data itself; a total of 6 transactions. (We can't put all of this in just one transaction +//because the D/C line needs to be toggled in the middle.) +//This routine queues these commands up so they get sent as quickly as possible. +void send_line(spi_device_handle_t spi, int ypos, uint16_t *line) +{ + esp_err_t ret; + int x; + //Transaction descriptors. Declared static so they're not allocated on the stack; we need this memory even when this + //function is finished because the SPI driver needs access to it even while we're already calculating the next line. + static spi_transaction_t trans[6]; + + //In theory, it's better to initialize trans and data only once and hang on to the initialized + //variables. We allocate them on the stack, so we need to re-init them each call. + for (x=0; x<6; x++) { + memset(&trans[x], 0, sizeof(spi_transaction_t)); + if ((x&1)==0) { + //Even transfers are commands + trans[x].length=8; + trans[x].user=(void*)0; + } else { + //Odd transfers are data + trans[x].length=8*4; + trans[x].user=(void*)1; + } + trans[x].flags=SPI_TRANS_USE_TXDATA; + } + trans[0].tx_data[0]=0x2A; //Column Address Set + trans[1].tx_data[0]=0; //Start Col High + trans[1].tx_data[1]=0; //Start Col Low + trans[1].tx_data[2]=(320)>>8; //End Col High + trans[1].tx_data[3]=(320)&0xff; //End Col Low + trans[2].tx_data[0]=0x2B; //Page address set + trans[3].tx_data[0]=ypos>>8; //Start page high + trans[3].tx_data[1]=ypos&0xff; //start page low + trans[3].tx_data[2]=(ypos+1)>>8; //end page high + trans[3].tx_data[3]=(ypos+1)&0xff; //end page low + trans[4].tx_data[0]=0x2C; //memory write + trans[5].tx_buffer=line; //finally send the line data + trans[5].length=320*2*8; //Data length, in bits + trans[5].flags=0; //undo SPI_TRANS_USE_TXDATA flag + + //Queue all transactions. + for (x=0; x<6; x++) { + ret=spi_device_queue_trans(spi, &trans[x], portMAX_DELAY); + assert(ret==ESP_OK); + } + + //When we are here, the SPI driver is busy (in the background) getting the transactions sent. That happens + //mostly using DMA, so the CPU doesn't have much to do here. We're not going to wait for the transaction to + //finish because we may as well spend the time calculating the next line. When that is done, we can call + //send_line_finish, which will wait for the transfers to be done and check their status. +} + + +void send_line_finish(spi_device_handle_t spi) +{ + spi_transaction_t *rtrans; + esp_err_t ret; + //Wait for all 6 transactions to be done and get back the results. + for (int x=0; x<6; x++) { + ret=spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY); + assert(ret==ESP_OK); + //We could inspect rtrans now if we received any info back. The LCD is treated as write-only, though. + } +} + + +//Simple routine to generate some patterns and send them to the LCD. Don't expect anything too +//impressive. Because the SPI driver handles transactions in the background, we can calculate the next line +//while the previous one is being sent. +void display_pretty_colors(spi_device_handle_t spi) +{ + uint16_t line[2][320]; + int x, y, frame=0; + //Indexes of the line currently being sent to the LCD and the line we're calculating. + int sending_line=-1; + int calc_line=0; + + while(1) { + frame++; + for (y=0; y<240; y++) { + //Calculate a line. + for (x=0; x<320; x++) { + line[calc_line][x]=((x<<3)^(y<<3)^(frame+x*y)); + } + //Finish up the sending process of the previous line, if any + if (sending_line!=-1) send_line_finish(spi); + //Swap sending_line and calc_line + sending_line=calc_line; + calc_line=(calc_line==1)?0:1; + //Send the line we currently calculated. + send_line(spi, y, line[sending_line]); + //The line is queued up for sending now; the actual sending happens in the + //background. We can go on to calculate the next line as long as we do not + //touch line[sending_line]; the SPI sending process is still reading from that. + } + } +} + + +void app_main() +{ + esp_err_t ret; + spi_device_handle_t spi; + spi_bus_config_t buscfg={ + .miso_io_num=PIN_NUM_MISO, + .mosi_io_num=PIN_NUM_MOSI, + .sclk_io_num=PIN_NUM_CLK, + .quadwp_io_num=-1, + .quadhd_io_num=-1 + }; + spi_device_interface_config_t devcfg={ + .clock_speed_hz=10000000, //Clock out at 10 MHz + .mode=0, //SPI mode 0 + .spics_io_num=PIN_NUM_CS, //CS pin + .queue_size=7, //We want to be able to queue 7 transactions at a time + .pre_cb=ili_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line + }; + //Initialize the SPI bus + ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1); + assert(ret==ESP_OK); + //Attach the LCD to the SPI bus + ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi); + assert(ret==ESP_OK); + //Initialize the LCD + ili_init(spi); + //Go do nice stuff. + display_pretty_colors(spi); +} diff --git a/examples/27_sd_card/Makefile b/examples/27_sd_card/Makefile new file mode 100644 index 0000000000..512e40fa67 --- /dev/null +++ b/examples/27_sd_card/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := sd_card + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/27_sd_card/README.md b/examples/27_sd_card/README.md new file mode 100644 index 0000000000..bfc6a39fe9 --- /dev/null +++ b/examples/27_sd_card/README.md @@ -0,0 +1,92 @@ +# SD Card example + +This example demonstrates how to use an SD card with ESP32. Example does the following steps: + +1. Use an "all-in-one" `esp_vfs_fat_sdmmc_mount` function to: + - initialize SDMMC peripheral, + - probe and initialize the card connected to SD/MMC slot 1 (HS2_CMD, HS2_CLK, HS2_D0, HS2_D1, HS2_D2, HS2_D3 lines), + - mount FAT filesystem using FATFS library (and format card, if the filesystem can not be mounted), + - register FAT filesystem in VFS, enabling C standard library and POSIX functions to be used. +2. Print information about the card, such as name, type, capacity, and maximum supported frequency. +3. Create a file using `fopen` and write to it using `fprintf`. +4. Rename the file. Before renaming, check if destination file already exists using `stat` function, and remove it using `unlink` function. +5. Open renamed file for reading, read back the line, and print it to the terminal. + +*Note:* despite the name, `sdmmc` component doesn't support MMC/eMMC cards yet. It is also possible to extend `sdmmc` component to support SPI mode with SD cards via SPI peripheral. + +## Hardware + +To run this example, ESP32 development board needs to be connected to SD card as follows: + +ESP32 pin | SD card pin | Notes +--------------|-------------|------------ +GPIO14 (MTMS) | CLK | 10k pullup +GPIO15 (MTDO) | CMD | 10k pullup +GPIO2 | D0 | 10k pullup, pull low to go into download mode +GPIO4 | D1 | 10k pullup; not used in 1-line mode +GPIO12 (MTDI) | D2 | otherwise 10k pullup (see note below!); not used in 1-line mode +GPIO13 (MTCK) | D3 | 10k pullup needed at card side, even in 1-line mode +N/C | CD | +N/C | WP | + +This example doesn't utilize card detect (CD) and write protect (WP) signals from SD card slot. + +### Note about GPIO2 +GPIO2 pin is used as a bootstrapping pin, and should be low to enter UART download mode. One way to do this is to connect GPIO0 and GPIO2 using a jumper, and then the auto-reset circuit on most development boards will pull GPIO2 low along with GPIO2, when entering download mode. + +### Note about GPIO12 + +GPIO12 is used as a bootstrapping pin to select output voltage of an internal regulator which powers the flash chip. + +- For boards which don't use the internal regulator, GPIO12 can be pulled high. +- On boards which use the internal regulator and a 3.3V flash chip, GPIO12 should be pulled up high, which is compatible with SD card operation. +- For boards which use 3.3V flash chip, GPIO12 needs to be low at reset. + * In this case, internal pullup can be enabled using a `gpio_pullup_en(GPIO_NUM_12);` call. Most SD cards work fine when an internal pullup on GPIO12 line is enabled. Note that if ESP32 experiences a power-on reset while the SD card is sending data, high level on GPIO12 can be latched into the bootstrapping register, and ESP32 will enter a boot loop until external reset with correct GPIO12 level is applied. + * Another option is to program flash voltage selection efuses: set `XPD_SDIO_TIEH=1`, `XPD_SDIO_FORCE=1`, and `XPD_SDIO_REG = 1`. This will permanently select 3.3V output voltage for the internal regulator, and GPIO12 will not be used as a bootstrapping pin anymore. Then it is safe to connect a pullup resistor to GPIO12. This option is suggested for production use. + +The following commands can be used to program flash voltage selection efuses **to 3.3V**: + +```sh + # Override flash regulator configuration using efuses + components/esptool_py/esptool/espefuse.py burn_efuse XPD_SDIO_FORCE + # Select 3.3V output voltage + components/esptool_py/esptool/espefuse.py burn_efuse XPD_SDIO_TIEH + # Enable internal voltage regulator + components/esptool_py/esptool/espefuse.py burn_efuse XPD_SDIO_REG +``` + +## 4-line and 1-line modes + +By default, example code uses the following initializer for SDMMC host peripheral configuration: + +```c++ +sdmmc_host_t host = SDMMC_HOST_DEFAULT(); +``` + +Among other things, this sets `host.flags` to `SDMMC_HOST_FLAG_4BIT`, which means that SD/MMC driver will switch to 4-line mode when initializing the card (initial communication always happens in 1-line mode). If some of D1, D2, D3 pins are not connected to the card, set `host.flags` to `SDMMC_HOST_FLAG_1BIT` — then the SD/MMC driver will not attempt to switch to 4-line mode. +Note that even if D3 line is not connected to the ESP32, it still has to be pulled up at card side, otherwise the card will go into SPI protocol mode. + + +## Example output + +Here is an example console output. In this case a 128MB SDSC card was connected, and `format_if_mount_failed` parameter was set to `true` in the source code. Card was unformatted, so the initial mount has failed. Card was then partitioned, formatted, and mounted again. + +``` +I (1776) example: Initializing SD card +W (1856) vfs_fat_sdmmc: failed to mount card (13) +W (1856) vfs_fat_sdmmc: partitioning card +W (1856) vfs_fat_sdmmc: formatting card +W (2726) vfs_fat_sdmmc: mounting again +I (2736) example: Card info: +I (2736) example: Name: SU128 +I (2736) example: Type: SDSC +I (2736) example: Capacity: 120 MB +I (2736) example: Max clock speed: 25 MHz +I (2736) example: Opening file +I (2756) example: File written +I (2756) example: Renaming file +I (2756) example: Reading file +I (2756) example: Read from file: 'Hello SU128!' +I (2756) example: Card unmounted +``` + diff --git a/examples/27_sd_card/main/component.mk b/examples/27_sd_card/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/27_sd_card/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/27_sd_card/main/sd_card.c b/examples/27_sd_card/main/sd_card.c new file mode 100644 index 0000000000..03d0ec1310 --- /dev/null +++ b/examples/27_sd_card/main/sd_card.c @@ -0,0 +1,107 @@ +/* SD card and FAT filesystem example. + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_vfs_fat.h" +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" + +static const char* TAG = "example"; + +void app_main(void) +{ + ESP_LOGI(TAG, "Initializing SD card"); + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + + // To use 1-line SD mode, uncomment the following line: + // host.flags = SDMMC_HOST_FLAG_1BIT; + + // This initializes the slot without card detect (CD) and write protect (WP) signals. + // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + // 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 = 5 + }; + + // Use settings defined above to initialize SD card and mount FAT filesystem. + // 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. + sdmmc_card_t* card; + esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card); + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + ESP_LOGE(TAG, "Failed to mount filesystem. If you want the card to be formatted, set format_if_mount_failed = true."); + } else { + ESP_LOGE(TAG, "Failed to initialize the card (%d). Make sure SD card lines have pull-up resistors in place.", ret); + } + return; + } + + // Card has been initialized, print its properties + sdmmc_card_print_info(stdout, card); + + // Use POSIX and C standard library functions to work with files. + // First create a file. + ESP_LOGI(TAG, "Opening file"); + FILE* f = fopen("/sdcard/hello.txt", "w"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for writing"); + return; + } + fprintf(f, "Hello %s!\n", card->cid.name); + fclose(f); + ESP_LOGI(TAG, "File written"); + + // Check if destination file exists before renaming + struct stat st; + if (stat("/sdcard/foo.txt", &st) == 0) { + // Delete it if it exists + unlink("/sdcard/foo.txt"); + } + + // Rename original file + ESP_LOGI(TAG, "Renaming file"); + if (rename("/sdcard/hello.txt", "/sdcard/foo.txt") != 0) { + ESP_LOGE(TAG, "Rename failed"); + return; + } + + // Open renamed file for reading + ESP_LOGI(TAG, "Reading file"); + f = fopen("/sdcard/foo.txt", "r"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for reading"); + return; + } + char line[64]; + fgets(line, sizeof(line), f); + fclose(f); + // strip newline + char* pos = strchr(line, '\n'); + if (pos) { + *pos = '\0'; + } + ESP_LOGI(TAG, "Read from file: '%s'", line); + + // All done, unmount partition and disable SDMMC host peripheral + esp_vfs_fat_sdmmc_unmount(); + ESP_LOGI(TAG, "Card unmounted"); +} diff --git a/examples/29_ledc/Makefile b/examples/29_ledc/Makefile new file mode 100644 index 0000000000..675fc8a38b --- /dev/null +++ b/examples/29_ledc/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ledc + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/29_ledc/README.md b/examples/29_ledc/README.md new file mode 100644 index 0000000000..9feea6b25b --- /dev/null +++ b/examples/29_ledc/README.md @@ -0,0 +1,16 @@ +# LEDC(LED control) Example + +###This example shows: + + * init LEDC module: + + a. You need to set the timer of LEDC first, this decide the frequency and resolution of PWM. + + b. You need to set the LEDC channel you want to use, and bind the channel with one of the timers. + + * You can install a default fade function, then you can use fade APIs. + + * You can also set a target duty directly without fading. + + * This example use GPIO18/19/4/5 as LEDC ouput, and it will change the duty repeatedly. + diff --git a/examples/29_ledc/main/component.mk b/examples/29_ledc/main/component.mk new file mode 100644 index 0000000000..44bd2b5273 --- /dev/null +++ b/examples/29_ledc/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/29_ledc/main/ledc_fade.c b/examples/29_ledc/main/ledc_fade.c new file mode 100644 index 0000000000..dcdb0462f6 --- /dev/null +++ b/examples/29_ledc/main/ledc_fade.c @@ -0,0 +1,131 @@ +/* Ledc fade example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/xtensa_api.h" +#include "freertos/queue.h" +#include "driver/ledc.h" +#include "esp_attr.h" +#include "esp_err.h" + +/* + * About this example + * 1. init LEDC module: + * a. You need to set the timer of LEDC first, this decide the frequency and resolution of PWM. + * b. You need to set the LEDC channel you want to use, and bind with one of the timers. + * + * 2. You can install a default fade function, then you can use fade APIs. + * + * 3. You can also set a target duty directly without fading. + * + * 4. This example use GPIO18/19/4/5 as LEDC ouput, and it will change the duty repeatedly. + * + * + */ + +#define LEDC_IO_0 (18) +#define LEDC_IO_1 (19) +#define LEDC_IO_2 (4) +#define LEDC_IO_3 (5) + +esp_err_t app_main() +{ + ledc_timer_config_t ledc_timer = { + //set timer counter bit number + .bit_num = LEDC_TIMER_13_BIT, + //set frequency of pwm + .freq_hz = 5000, + //timer mode, + .speed_mode = LEDC_HIGH_SPEED_MODE, + //timer index + .timer_num = LEDC_TIMER_0 + }; + ledc_timer_config(&ledc_timer); + + ledc_channel_config_t ledc_channel = { + //set LEDC channel 0 + .channel = LEDC_CHANNEL_0, + //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) + .duty = 100, + //GPIO number + .gpio_num = LEDC_IO_0, + //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. + .intr_type = LEDC_INTR_FADE_END, + //set LEDC mode, from ledc_mode_t + .speed_mode = LEDC_HIGH_SPEED_MODE, + //set LEDC timer source, if different channel use one timer, + //the frequency and bit_num of these channels should be the same + .timer_sel = LEDC_TIMER_0 + }; + //set the configuration + ledc_channel_config(&ledc_channel); + + //config ledc channel1 + ledc_channel.channel = LEDC_CHANNEL_1; + ledc_channel.gpio_num = LEDC_IO_1; + ledc_channel_config(&ledc_channel); + //config ledc channel2 + ledc_channel.channel = LEDC_CHANNEL_2; + ledc_channel.gpio_num = LEDC_IO_2; + ledc_channel_config(&ledc_channel); + //config ledc channel3 + ledc_channel.channel = LEDC_CHANNEL_3; + ledc_channel.gpio_num = LEDC_IO_3; + ledc_channel_config(&ledc_channel); + + //initialize fade service. + ledc_fade_func_install(0); + + while(1) { + printf("LEDC fade up\n"); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 1000, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 7000, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 5000, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 3000, 2000); + ledc_fade_start(LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_1, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_2, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_3, LEDC_FADE_NO_WAIT); + vTaskDelay(3000 / portTICK_PERIOD_MS); + + printf("LEDC fade down\n"); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 100, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 300, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 500, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 700, 2000); + ledc_fade_start(LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_1, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_2, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_3, LEDC_FADE_NO_WAIT); + vTaskDelay(3000 / portTICK_PERIOD_MS); + + printf("LEDC set duty without fade\n"); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 1000); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 7000); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 5000); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 3000); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3); + vTaskDelay(2000 / portTICK_PERIOD_MS); + + printf("LEDC set duty without fade\n"); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 0); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 0); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 0); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 0); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3); + vTaskDelay(2000 / portTICK_PERIOD_MS); + } +} diff --git a/examples/30_mdns_example/Makefile b/examples/30_mdns_example/Makefile new file mode 100644 index 0000000000..0353c51c0e --- /dev/null +++ b/examples/30_mdns_example/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := mdns-test + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/30_mdns_example/README.md b/examples/30_mdns_example/README.md new file mode 100644 index 0000000000..1c298604b8 --- /dev/null +++ b/examples/30_mdns_example/README.md @@ -0,0 +1,5 @@ +# 30_mdns example + +Shows how to use mDNS to advertise lookup services and hosts + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/30_mdns_example/main/Kconfig.projbuild b/examples/30_mdns_example/main/Kconfig.projbuild new file mode 100644 index 0000000000..3122e0309c --- /dev/null +++ b/examples/30_mdns_example/main/Kconfig.projbuild @@ -0,0 +1,29 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "myssid" + help + WiFi password (WPA or WPA2) for the example to use. + + Can be left blank if the network has no security set. + +config MDNS_HOSTNAME + string "mDNS Hostname" + default "esp32-mdns" + help + mDNS Hostname for example to use + +config MDNS_INSTANCE + string "mDNS Instance Name" + default "ESP32 with mDNS" + help + mDNS Instance Name for example to use + +endmenu \ No newline at end of file diff --git a/examples/30_mdns_example/main/component.mk b/examples/30_mdns_example/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/30_mdns_example/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/30_mdns_example/main/mdns_example_main.c b/examples/30_mdns_example/main/mdns_example_main.c new file mode 100644 index 0000000000..d19fd1ae68 --- /dev/null +++ b/examples/30_mdns_example/main/mdns_example_main.c @@ -0,0 +1,184 @@ +/* MDNS-SD Query and advertise Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "mdns.h" + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define EXAMPLE_MDNS_HOSTNAME CONFIG_MDNS_HOSTNAME +#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +static const char *TAG = "mdns-test"; + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_CONNECTED: + /* enable ipv6 */ + tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +static void query_mdns_service(mdns_server_t * mdns, const char * service, const char * proto) +{ + if(!mdns) { + return; + } + uint32_t res; + if (!proto) { + ESP_LOGI(TAG, "Host Lookup: %s", service); + res = mdns_query(mdns, service, 0, 1000); + if (res) { + size_t i; + for(i=0; iaddr), IPV62STR(r->addrv6)); + } + } + mdns_result_free(mdns); + } else { + ESP_LOGI(TAG, " Not Found"); + } + } else { + ESP_LOGI(TAG, "Service Lookup: %s.%s ", service, proto); + res = mdns_query(mdns, service, proto, 1000); + if (res) { + size_t i; + for(i=0; ihost)?r->host:"", (r->instance)?r->instance:"", + IP2STR(&r->addr), IPV62STR(r->addrv6), + r->port, (r->txt)?r->txt:""); + } + } + mdns_result_free(mdns); + } + } +} + +static void mdns_task(void *pvParameters) +{ + mdns_server_t * mdns = NULL; + while(1) { + /* Wait for the callback to set the CONNECTED_BIT in the + event group. + */ + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + ESP_LOGI(TAG, "Connected to AP"); + + if (!mdns) { + esp_err_t err = mdns_init(TCPIP_ADAPTER_IF_STA, &mdns); + if (err) { + ESP_LOGE(TAG, "Failed starting MDNS: %u", err); + continue; + } + + ESP_ERROR_CHECK( mdns_set_hostname(mdns, EXAMPLE_MDNS_HOSTNAME) ); + ESP_ERROR_CHECK( mdns_set_instance(mdns, EXAMPLE_MDNS_INSTANCE) ); + + const char * arduTxtData[4] = { + "board=esp32", + "tcp_check=no", + "ssh_upload=no", + "auth_upload=no" + }; + + ESP_ERROR_CHECK( mdns_service_add(mdns, "_arduino", "_tcp", 3232) ); + ESP_ERROR_CHECK( mdns_service_txt_set(mdns, "_arduino", "_tcp", 4, arduTxtData) ); + ESP_ERROR_CHECK( mdns_service_add(mdns, "_http", "_tcp", 80) ); + ESP_ERROR_CHECK( mdns_service_instance_set(mdns, "_http", "_tcp", "ESP32 WebServer") ); + ESP_ERROR_CHECK( mdns_service_add(mdns, "_smb", "_tcp", 445) ); + } else { + query_mdns_service(mdns, "esp32", NULL); + query_mdns_service(mdns, "_arduino", "_tcp"); + query_mdns_service(mdns, "_http", "_tcp"); + query_mdns_service(mdns, "_printer", "_tcp"); + query_mdns_service(mdns, "_ipp", "_tcp"); + query_mdns_service(mdns, "_afpovertcp", "_tcp"); + query_mdns_service(mdns, "_smb", "_tcp"); + query_mdns_service(mdns, "_ftp", "_tcp"); + query_mdns_service(mdns, "_nfs", "_tcp"); + } + + ESP_LOGI(TAG, "Restarting in 10 seconds!"); + vTaskDelay(10000 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "Starting again!"); + } +} + +void app_main() +{ + nvs_flash_init(); + initialise_wifi(); + xTaskCreate(&mdns_task, "mdns_task", 2048, NULL, 5, NULL); +} diff --git a/examples/31_wpa2_enterprise/Makefile b/examples/31_wpa2_enterprise/Makefile new file mode 100644 index 0000000000..ff23a93934 --- /dev/null +++ b/examples/31_wpa2_enterprise/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := wpa2-enterprise + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/31_wpa2_enterprise/README.md b/examples/31_wpa2_enterprise/README.md new file mode 100644 index 0000000000..f84f9e070a --- /dev/null +++ b/examples/31_wpa2_enterprise/README.md @@ -0,0 +1,76 @@ +# WPA2 Enterprise Example + +This example shows how ESP32 connects to AP with wpa2 enterprise encryption. Example does the following steps: + +1. Install CA certificate which is optional. +2. Install client certificate and client key which is required in TLS method and optional in PEAP and TTLS methods. +3. Set identity of phase 1 which is optional. +4. Set user name and password of phase 2 which is required in PEAP and TTLS methods. +5. Enable wpa2 enterprise. +6. Connect to AP. + +*Note:* certificate currently is generated when compiling the example and then stored in flash. + +## The file wpa2_ca.pem, wpa2_ca.key, wpa2_server.pem, wpa2_server.crt and wpa2_server.key can be used to configure AP with + wpa2 enterprise encryption. The steps how to generate new certificates and keys using openssl is as follows: + +1. wpa2_ca.pem wpa2_ca.key: + openssl req -new -x509 -keyout wpa2_ca.key -out wpa2_ca.pem +2. wpa2_server.key: + openssl req -new -key wpa2_server.key -out wpa2_server.csr +3. wpa2_csr: + openssl req -new -key server.key -out server.csr +4. wpa2_server.crt: + openssl ca -batch -keyfile wpa2_ca.key -cert wpa2_ca.pem -in wpa2_server.csr -key ca1234 -out wpa2_server.crt -extensions xpserver_ext -extfile xpextensions +5. wpa2_server.p12: + openssl pkcs12 -export -in wpa2_server.crt -inkey wpa2_server.key -out wpa2_server.p12 -passin pass:sv1234 -passout pass:sv1234 +6. wpa2_server.pem: + openssl pkcs12 -in wpa2_server.p12 -out wpa2_server.pem -passin pass:sv1234 -passout pass:sv1234 +7. wpa2_client.key: + openssl genrsa -out wpa2_client.key 1024 +8. wpa2_client.csr: + openssl req -new -key wpa2_client.key -out wpa2_client.csr +9. wpa2_client.crt: + openssl ca -batch -keyfile wpa2_ca.key -cert wpa2_ca.pem -in wpa2_client.csr -key ca1234 -out wpa2_client.crt -extensions xpclient_ext -extfile xpextensions +10. wpa2_client.p12: + openssl pkcs12 -export -in wpa2_client.crt -inkey wpa2_client.key -out wpa2_client.p12 +11. wpa2_client.pem: + openssl pkcs12 -in wpa2_client.p12 -out wpa2_client.pem + +### Example output + +Here is an example of wpa2 enterprise(PEAP method) console output. + +I (1352) example: Setting WiFi configuration SSID wpa2_test... +I (1362) wpa: WPA2 ENTERPRISE VERSION: [v2.0] enable + +I (1362) wifi: rx_ba=1 tx_ba=1 + +I (1372) wifi: mode : sta (24:0a:c4:03:b8:dc) +I (3002) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:11 +I (3642) wifi: state: init -> auth (b0) +I (3642) wifi: state: auth -> assoc (0) +I (3652) wifi: state: assoc -> run (10) +I (3652) wpa: wpa2_task prio:24, stack:6144 + +I (3972) wpa: >>>>>wpa2 FINISH + +I (3982) wpa: wpa2 task delete + +I (3992) wifi: connected with wpa2_test, channel 11 +I (5372) example: ~~~~~~~~~~~ +I (5372) example: IP:0.0.0.0 +I (5372) example: MASK:0.0.0.0 +I (5372) example: GW:0.0.0.0 +I (5372) example: ~~~~~~~~~~~ +I (6832) event: ip: 192.168.1.112, mask: 255.255.255.0, gw: 192.168.1.1 +I (7372) example: ~~~~~~~~~~~ +I (7372) example: IP:192.168.1.112 +I (7372) example: MASK:255.255.255.0 +I (7372) example: GW:192.168.1.1 +I (7372) example: ~~~~~~~~~~~ +I (9372) example: ~~~~~~~~~~~ +I (9372) example: IP:192.168.1.112 +I (9372) example: MASK:255.255.255.0 +I (9372) example: GW:192.168.1.1 +I (9372) example: ~~~~~~~~~~~ diff --git a/examples/31_wpa2_enterprise/main/Kconfig.projbuild b/examples/31_wpa2_enterprise/main/Kconfig.projbuild new file mode 100644 index 0000000000..06fab0bfa1 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/Kconfig.projbuild @@ -0,0 +1,34 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "wpa2_test" + help + SSID (network name) for the example to connect to. + +config EAP_METHOD + int "EAP METHOD" + default 1 + help + EAP method (TLS, PEAP or TTLS) for the example to use. + TLS: 0, PEAP: 1, TTLS: 2 + +config EAP_ID + string "EAP ID" + default "example@espressif.com" + help + Identity in phase 1 of EAP procedure. + +config EAP_USERNAME + string "EAP USERNAME" + default "espressif" + help + Username for EAP method (PEAP and TTLS). + +config EAP_PASSWORD + string "EAP PASSWORD" + default "test11" + help + Password for EAP method (PEAP and TTLS). + +endmenu \ No newline at end of file diff --git a/examples/31_wpa2_enterprise/main/component.mk b/examples/31_wpa2_enterprise/main/component.mk new file mode 100644 index 0000000000..aab8ff8f38 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/component.mk @@ -0,0 +1,12 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +# embed files from the "certs" directory as binary data symbols +# in the app +COMPONENT_EMBED_TXTFILES := wpa2_ca.pem +COMPONENT_EMBED_TXTFILES += wpa2_client.crt +COMPONENT_EMBED_TXTFILES += wpa2_client.key + + diff --git a/examples/31_wpa2_enterprise/main/wpa2_ca.pem b/examples/31_wpa2_enterprise/main/wpa2_ca.pem new file mode 100644 index 0000000000..c36b97e974 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_ca.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3DCCA0WgAwIBAgIJANe5ZSCKoB8fMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD +VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT +BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs +ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X +DTE2MTEyMzAyNTUwN1oXDTE3MDEyMjAyNTUwN1owgZMxCzAJBgNVBAYTAkZSMQ8w +DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh +bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG +A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBAL03y7N2GvNDO9BN8fVtdNonp0bMiqpj1D0He5+OTM+9 +3ZTIsJCNrbzhLQrRI3vMW7UDy8U7GeWORN9W4dWYlYiy/NFRp3hNMrbePhVmNIOV +ww4ovGzbD+Xo31gPVkhzQ8I5/jbOIQBmgKMAMZyOMlG9VD6yMmAeYqnZYz68WHKt +AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUf1MLQIzAEZcRsgZlS8sosfmVI+UwgcgG +A1UdIwSBwDCBvYAUf1MLQIzAEZcRsgZlS8sosfmVI+WhgZmkgZYwgZMxCzAJBgNV +BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG +A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl +LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDX +uWUgiqAfHzAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93 +d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA +GepHc7TE/P+5t/cZPn5TTQkWQ/4/1lgQd82lF36RYWSIW3BdAc0zwYWYZaWixxyp +s0YOqwz6PZAGRV+SlYO2f8Kf+C3aZs4YHB0GsmksmFOb8r9d7xcDuOKHoA+QV0Zw +RaK6pttsBAxy7rw3kX/CgTp0Y2puaLdMXv/v9FisCP8= +-----END CERTIFICATE----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_client.crt b/examples/31_wpa2_enterprise/main/wpa2_client.crt new file mode 100644 index 0000000000..7499e6967a --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_client.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 44 (0x2c) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority + Validity + Not Before: Nov 23 02:55:07 2016 GMT + Not After : Jan 22 02:55:07 2017 GMT + Subject: C=FR, ST=Radius, O=Example Inc., CN=user@example.com/emailAddress=user@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ac:41:d4:a2:46:0c:dc:67:1d:7b:89:36:7c:15: + be:a2:c1:fe:4c:f2:fa:af:5d:76:0e:ee:b5:ca:d4: + d3:01:c8:6b:30:50:df:2d:57:17:f4:43:47:97:ca: + f1:8d:f7:c0:9d:56:b3:e7:17:7c:58:59:de:f3:be: + b5:08:5d:f8:3a:ad:83:44:0d:31:c9:f1:3d:f1:9a: + cf:84:0c:ad:d3:be:5c:bd:3d:58:b5:1d:2c:fe:70: + 8d:c5:b0:17:87:d4:8e:85:f7:51:4c:0f:d1:e0:8c: + 7b:a0:25:ab:91:7c:7f:eb:47:73:c9:4b:6c:8b:e6: + c1:06:d5:94:30:63:ec:45:1a:f5:7f:46:2f:b3:84: + 78:5d:1c:37:1a:fa:57:ea:45:5e:45:40:ab:14:c7: + 81:b0:26:3d:7e:cf:da:db:f0:f1:40:a7:a1:4b:54: + f3:96:1b:c9:30:3c:3c:d8:19:ba:c7:df:b1:ad:a2: + d6:17:0a:d6:ed:31:b5:cb:12:39:f5:6e:92:6b:85: + f2:9e:c7:06:6b:bb:89:ed:a7:5f:ec:56:12:46:fd: + 3a:74:d1:d2:31:30:1d:58:19:25:33:ff:11:ea:3a: + 52:33:b1:fb:d3:75:8d:1f:5e:36:a5:35:e0:11:5a: + 4a:2d:97:58:2c:3d:62:3c:32:af:83:69:a9:1a:32: + 1b:b7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.example.com/example_ca.crl + + Signature Algorithm: sha1WithRSAEncryption + 8b:8d:b6:19:ce:6f:6b:9e:1d:03:8b:6b:10:fc:99:d0:7a:2f: + e0:37:ce:b8:a4:e4:b9:a1:c2:36:ff:76:b2:ad:d7:d0:df:d1: + 03:27:93:a7:4e:1e:bf:ed:d2:b7:65:2a:c9:c3:ab:20:aa:e3: + 10:4c:75:3b:c4:02:ab:34:08:6e:61:91:cf:e3:02:35:6a:e5: + f3:25:96:51:92:82:6e:52:81:c1:f1:7b:68:02:b0:ce:f4:ba: + fd:6e:68:35:b3:7e:77:cb:a0:1e:11:5e:58:bf:f3:2a:ed:b3: + 4c:82:21:5e:1b:47:b6:2f:f3:f5:c9:1b:6a:70:44:6d:ff:ad: + a6:e3 +-----BEGIN CERTIFICATE----- +MIIDTjCCAregAwIBAgIBLDANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjExMjMw +MjU1MDdaFw0xNzAxMjIwMjU1MDdaMHExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEZMBcGA1UEAwwQdXNlckBleGFt +cGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdXNlckBleGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKxB1KJGDNxnHXuJNnwVvqLB/kzy+q9d +dg7utcrU0wHIazBQ3y1XF/RDR5fK8Y33wJ1Ws+cXfFhZ3vO+tQhd+Dqtg0QNMcnx +PfGaz4QMrdO+XL09WLUdLP5wjcWwF4fUjoX3UUwP0eCMe6Alq5F8f+tHc8lLbIvm +wQbVlDBj7EUa9X9GL7OEeF0cNxr6V+pFXkVAqxTHgbAmPX7P2tvw8UCnoUtU85Yb +yTA8PNgZusffsa2i1hcK1u0xtcsSOfVukmuF8p7HBmu7ie2nX+xWEkb9OnTR0jEw +HVgZJTP/Eeo6UjOx+9N1jR9eNqU14BFaSi2XWCw9Yjwyr4NpqRoyG7cCAwEAAaNP +ME0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov +L3d3dy5leGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOB +gQCLjbYZzm9rnh0Di2sQ/JnQei/gN864pOS5ocI2/3ayrdfQ39EDJ5OnTh6/7dK3 +ZSrJw6sgquMQTHU7xAKrNAhuYZHP4wI1auXzJZZRkoJuUoHB8XtoArDO9Lr9bmg1 +s353y6AeEV5Yv/Mq7bNMgiFeG0e2L/P1yRtqcERt/62m4w== +-----END CERTIFICATE----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_client.key b/examples/31_wpa2_enterprise/main/wpa2_client.key new file mode 100644 index 0000000000..0b8f0d0199 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEArEHUokYM3Gcde4k2fBW+osH+TPL6r112Du61ytTTAchrMFDf +LVcX9ENHl8rxjffAnVaz5xd8WFne8761CF34Oq2DRA0xyfE98ZrPhAyt075cvT1Y +tR0s/nCNxbAXh9SOhfdRTA/R4Ix7oCWrkXx/60dzyUtsi+bBBtWUMGPsRRr1f0Yv +s4R4XRw3GvpX6kVeRUCrFMeBsCY9fs/a2/DxQKehS1TzlhvJMDw82Bm6x9+xraLW +FwrW7TG1yxI59W6Sa4XynscGa7uJ7adf7FYSRv06dNHSMTAdWBklM/8R6jpSM7H7 +03WNH142pTXgEVpKLZdYLD1iPDKvg2mpGjIbtwIDAQABAoIBAQCMhO9GqUpYia2d +VyOhOcPX1dTzRMuHPwDN0aFvIwo2zB3UvkQxInkiA7hldWJz44W3VEFR5PDEyht8 +Tzgy6SVUCLOqUfEpwag8bYOXPxiWQRY6Mc8pf/FyZrLgb3PilFznoAcru0QEn9VB +oTlCZ4OalSE5NlQIFGemgZhvmTPmcm4OwPW2diBjLtb3AA8eaaw8okWZwr8g4Bcd +el5KX6pZpDRpGQueh3iKaKxYWbxLYK+c30gKWD65tsAqKyVg2Tm1R2c+kFXgizZt +EexD95SGMjSkGg3R05sKv6m71iJhlOzVQ4ZCKm18Kqa7wZuZ4SIehVmKIV0gaupz +gjyr7+NBAoGBAOGjjGI3nxJTZY3O+KeaQo/jqrKowqZxzMOsCgAvW56xDuAvx9TJ +m4428NGubMl/0RwX6TnxJDm6oe+tnOxLIgE/VnsQLiNzQuFJxrs5JYctdGc4uvk2 +KuXDr7tPEYlU/7OLRReov9emydIXJnsGejkIPllUj+DGNjNFqtXh2VoHAoGBAMNv +eSgJSkcM6AUaDuUKaXBL2nkKHNoTtRQ0eCEUds6arKyMo0mSP753FNEuOWToVz1O +oaddSFw81J9t+Xd6XSRbhMj63bQ9nvFKBA1lJfLu+xe3ts0f+vmp1PguOuUHsgNP +aAm/gLPSKUpBO46NG6KhUrZ2ej6AEg7SuGXrDITRAoGBAKK7s6m6d81dvGZ0GT23 +sb3Y8ul7cTdd59JPp77OaQOgqxvhGfxLkxcUZMa1R9xjhMsAK8MQOZIxGk2kJwL8 +hP/lUFfdKYmDvX6CGQQ6iOhfTg6MCb1m5bVkVr9+nSUw2mIBVclkeUftEK2m6Kfd +2hR774u5wzLXgYuk+TrcckfNAoGBAJ9X8hacjH0lnr8aIe7I8HLoxbZOcnuz+b4B +kbiW8M8++W6uNCw2G9b1THnJEG6fqRGJXPASdH8P8eQTTIUHtY2BOOCM+dqNK1xc +FrW9NJXAF+WcmmTgoEaTG9tGBirafV+JjK/1/b+fqJ6sVRzDHDcbBU9ThhQTY6XG +VSZz4H8hAoGBAMeQQjiUlKBnpGt1oTgKDZo58b7ui61yftg+dEAwIKs6eb5X20vZ +Ca4v/zg06k9lKTzyspQjJZuzpMjFUvDK4ReamEvmwQTIc+oYVJm9Af1HUytzrHJH +u0/dDt0eYpZpzrFqxlP+0oXxlegD8REMVvwNCy+4isyCvjogDaYRfJqi +-----END RSA PRIVATE KEY----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_client.pem b/examples/31_wpa2_enterprise/main/wpa2_client.pem new file mode 100644 index 0000000000..37bf709195 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_client.pem @@ -0,0 +1,57 @@ +Bag Attributes + localKeyID: E1 2F DD 9A 78 71 54 6D 59 57 AA 6A 9F 92 3B 5C CC AB A3 64 +subject=/C=FR/ST=Radius/O=Example Inc./CN=user@example.com/emailAddress=user@example.com +issuer=/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.com/CN=Example Certificate Authority +-----BEGIN CERTIFICATE----- +MIIDTjCCAregAwIBAgIBLDANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjExMjMw +MjU1MDdaFw0xNzAxMjIwMjU1MDdaMHExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEZMBcGA1UEAwwQdXNlckBleGFt +cGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdXNlckBleGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKxB1KJGDNxnHXuJNnwVvqLB/kzy+q9d +dg7utcrU0wHIazBQ3y1XF/RDR5fK8Y33wJ1Ws+cXfFhZ3vO+tQhd+Dqtg0QNMcnx +PfGaz4QMrdO+XL09WLUdLP5wjcWwF4fUjoX3UUwP0eCMe6Alq5F8f+tHc8lLbIvm +wQbVlDBj7EUa9X9GL7OEeF0cNxr6V+pFXkVAqxTHgbAmPX7P2tvw8UCnoUtU85Yb +yTA8PNgZusffsa2i1hcK1u0xtcsSOfVukmuF8p7HBmu7ie2nX+xWEkb9OnTR0jEw +HVgZJTP/Eeo6UjOx+9N1jR9eNqU14BFaSi2XWCw9Yjwyr4NpqRoyG7cCAwEAAaNP +ME0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov +L3d3dy5leGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOB +gQCLjbYZzm9rnh0Di2sQ/JnQei/gN864pOS5ocI2/3ayrdfQ39EDJ5OnTh6/7dK3 +ZSrJw6sgquMQTHU7xAKrNAhuYZHP4wI1auXzJZZRkoJuUoHB8XtoArDO9Lr9bmg1 +s353y6AeEV5Yv/Mq7bNMgiFeG0e2L/P1yRtqcERt/62m4w== +-----END CERTIFICATE----- +Bag Attributes + localKeyID: E1 2F DD 9A 78 71 54 6D 59 57 AA 6A 9F 92 3B 5C CC AB A3 64 +Key Attributes: +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIdQ9mH9ZzfJECAggA +MBQGCCqGSIb3DQMHBAhDtvRRe/rZHgSCBMhNLBUX5dw3KAHBXkTJDqa9EjOCbD0i +kMGxvoVE3Vrotoh5rMiGlPg+qOTaKT7kd3Yl6bwxF1sV0GyzuSJn+legjV9oXuVK +rI3NWeZr7KG18IP+ZGPs4fDRKnCiKK6cCopF1mzvRhzts5stlOqQkp1Dv9+A7Gyp +OarBK6x3p5db1mznOMdwzD8vyOxGhE98Zq6yDaDz1yKm6mtOLc8RQ1zbwEE1nkcc +J1R73A77E8dY6Rq7E4ec6d+rHki79yskq6eu30ECzC3VUSn6wdKXKKJgvn1V3dNf +QyRdMwXFVXySaGcBaq3zQp9qW+ISesvEstH9fN/GSzoDqe1OqTbe9pLCUtmVksAg +9z4et+OHYdk1c6X+5VI0ywg4t0qjOCOTacJSzw0/lC8OhNTH0jQDFAoIMOIvLuyO +pdNUcaGiWPKS1WjWEMiPPusrPiDXZ5T88go35rGnZJRUK6ighSdtKPKG3qPAslMo +Rn+eOiA2YJ5AorfkR63PI9MfCJbNVfmeTV/VoPXgjrtVNGtvrV54KeHwwLSJtyfj +xqS1g7aSgTgzfoTgYtzxVwy1g3jJZjYlOd6hKBj+Zzl/7C4cyilI3rrKU1OCokAn +xXFwKu2clrh1n3VHD+TdUP1lJD44uHuhgev2MmwOBGKGGNsGNqK9Jo7PCBeWyBSq +d0vzlbo95mMJ5BxKEJex3pxNeTTtfRBjr0AJLuneUBDvjSfhltIvOeMXEfIPQCtU +MTqjGmpgOTbMI4fEgprH7dULOHyn+Mn37sBMbtYHHk2D4FSJD2FkIP3KrudDf/Yr +ePn2ACne2ot9mW9McvDZGDOq0fyrIMcCNTwxP52Z3CuJucwuAoTwiwSg9ZM3t4E9 +K1Fz10xwzctFip9XxpxADA9M/QRa9VBgt7pt114Z2y+/ba0hOZ9mCrdpcHGH8xkO +kJhcCCg1tOLVl2AmUIVtdMDaJMgskq8tfeRTD6qp/JzKQCiz2OvcOG5ixgAhuOSn +9WjCnM4hARhZ/OVDYvsugwELil6E4zpc7mawtNiMj4P7ad1O+/hDN5yVQiU/XzIg +OJEV//FfnApcigay5Ne/n9K2morPIIDSQdet/2L6Y6DmtwM9B8qbzb/DjHfXK4nf +hvvPXD+DUCOfrj3VdkgwY338anWFFQpYbju8nrIv7+vINys+y8HeiPQp5I9zw1XF +q5tfEt4YfTO8hwqBNZL9yhS46CLgvvWavP1ZfvknvNqcvVvRMo60j0DDeZBW676v +U251knuWH16m+/cozbelscwZxikhzxdYzudWjFlH+phOIIvnmCgEZKtN8OWxFoN/ +6YBLPO1jOkibGKV1GHVg0PHdSwYmc1H5CefPobow9XP/RCd8C9+eBmynzErMmeme +4R2etPlsBt3mpakFoG7U+iNu89e4eOK2pzSrydfBSS6tUFXg5L7W5UrdWQ8vRyuc +aLkwuzdVbM8adlcAyb1MVc+1G5JMJVRfPNNAfJkSti7VUqONoOnoUAUSXGpNwoXa +ddctpxLmwsfjYEJ3OCLjFy06A2ZlikqtMi5H4sNytSp0Mfr06J4ZZmL8T1GHxYSP +Xf51VEqiZpKHeBo7ZqrxKvGvFxzm6mGMy8LPpRfSy88z4rPjmP5qrXTbo9qBeo9G +GlY= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_enterprise_main.c b/examples/31_wpa2_enterprise/main/wpa2_enterprise_main.c new file mode 100644 index 0000000000..7d325c76a0 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_enterprise_main.c @@ -0,0 +1,154 @@ +/* WiFi Connection Example using WPA2 Enterprise + * + * Original Copyright (C) 2006-2016, ARM Limited, All Rights Reserved, Apache 2.0 License. + * Additions Copyright (C) Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD, Apache 2.0 License. + * + * + * 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. + */ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_wifi.h" +#include "esp_wpa2.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "tcpip_adapter.h" + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" + + You can choose EAP method via 'make menuconfig' according to the + configuration of AP. +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_EAP_METHOD CONFIG_EAP_METHOD + +#define EXAMPLE_EAP_ID CONFIG_EAP_ID +#define EXAMPLE_EAP_USERNAME CONFIG_EAP_USERNAME +#define EXAMPLE_EAP_PASSWORD CONFIG_EAP_PASSWORD + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +/* Constants that aren't configurable in menuconfig */ +#define EAP_PEAP 1 +#define EAP_TTLS 2 + +static const char *TAG = "example"; + +/* CA cert, taken from wpa2_ca.pem + Client cert, taken from wpa2_client.crt + Client key, taken from wpa2_client.key + + The PEM, CRT and KEY file were provided by the person or organization + who configured the AP with wpa2 enterprise. + + To embed it in the app binary, the PEM, CRT and KEY file is named + in the component.mk COMPONENT_EMBED_TXTFILES variable. +*/ +extern uint8_t ca_pem_start[] asm("_binary_wpa2_ca_pem_start"); +extern uint8_t ca_pem_end[] asm("_binary_wpa2_ca_pem_end"); +extern uint8_t client_crt_start[] asm("_binary_wpa2_client_crt_start"); +extern uint8_t client_crt_end[] asm("_binary_wpa2_client_crt_end"); +extern uint8_t client_key_start[] asm("_binary_wpa2_client_key_start"); +extern uint8_t client_key_end[] asm("_binary_wpa2_client_key_end"); + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void initialise_wifi(void) +{ + unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start; + unsigned int client_crt_bytes = client_crt_end - client_crt_start; + unsigned int client_key_bytes = client_key_end - client_key_start; + + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_ca_cert(ca_pem_start, ca_pem_bytes) ); + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_cert_key(client_crt_start, client_crt_bytes,\ + client_key_start, client_key_bytes, NULL, 0) ); + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EXAMPLE_EAP_ID, strlen(EXAMPLE_EAP_ID)) ); + if (EXAMPLE_EAP_METHOD == EAP_PEAP || EXAMPLE_EAP_METHOD == EAP_TTLS) { + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EXAMPLE_EAP_USERNAME, strlen(EXAMPLE_EAP_USERNAME)) ); + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EXAMPLE_EAP_PASSWORD, strlen(EXAMPLE_EAP_PASSWORD)) ); + } + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_enable() ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +static void wpa2_enterprise_task(void *pvParameters) +{ + tcpip_adapter_ip_info_t ip; + memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + + while (1) { + vTaskDelay(2000 / portTICK_PERIOD_MS); + + if (tcpip_adapter_get_ip_info(ESP_IF_WIFI_STA, &ip) == 0) { + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "IP:"IPSTR, IP2STR(&ip.ip)); + ESP_LOGI(TAG, "MASK:"IPSTR, IP2STR(&ip.netmask)); + ESP_LOGI(TAG, "GW:"IPSTR, IP2STR(&ip.gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + } + } +} + +void app_main() +{ + nvs_flash_init(); + initialise_wifi(); + xTaskCreate(&wpa2_enterprise_task, "wpa2_enterprise_task", 4096, NULL, 5, NULL); +} diff --git a/examples/31_wpa2_enterprise/main/wpa2_server.crt b/examples/31_wpa2_enterprise/main/wpa2_server.crt new file mode 100644 index 0000000000..312a7d6aa8 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_server.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 43 (0x2b) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority + Validity + Not Before: Nov 23 02:55:07 2016 GMT + Not After : Jan 22 02:55:07 2017 GMT + Subject: C=FR, ST=Radius, O=Example Inc., CN=Example Server Certificate/emailAddress=admin@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ca:5d:86:b7:7a:3a:bc:f4:4d:d8:69:8c:25:bf: + d1:d7:33:b7:76:ea:d9:ae:b6:78:14:d6:d5:ca:67: + fd:f4:5c:13:d3:01:b4:bc:92:d5:a0:51:f5:fe:81: + 6f:da:28:33:07:08:20:e9:26:27:c6:ab:43:0b:7f: + ce:7c:3b:c6:9c:a4:6c:85:22:3d:40:84:27:32:d6: + a2:94:ed:14:29:4c:ef:d9:ac:d5:a3:ea:7d:47:76: + 18:57:7c:0a:4a:fe:ba:8f:b8:44:44:a5:62:e8:b0: + dd:59:6b:d2:20:69:f1:64:e1:f6:d0:e5:9e:88:da: + 10:e5:58:18:fc:87:ce:2f:67:f6:9d:f8:ac:da:0f: + 2b:f5:58:30:04:13:1c:b5:71:ce:3d:26:c7:34:03: + 66:38:ca:8d:11:75:f0:0b:14:ab:98:b1:dc:cd:81: + d2:68:33:96:d6:50:4f:a7:19:d0:20:15:5e:e0:18: + 8b:07:83:11:2d:3d:51:14:68:73:cd:f2:70:c6:59: + 50:cf:e1:f5:12:88:d5:71:de:1d:92:2e:7d:d1:8b: + 09:fe:b4:17:bd:7e:73:07:c0:a1:6a:f3:af:80:3b: + e4:d7:62:6d:1c:15:93:92:47:25:bd:f6:50:02:3e: + 9c:00:7d:15:89:f2:38:10:95:f2:ef:09:fa:b5:cf: + 90:63 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.example.com/example_ca.crl + + Signature Algorithm: sha1WithRSAEncryption + 92:f8:06:4b:32:4f:f1:22:18:9c:67:dc:28:03:82:ee:14:0a: + 59:a9:07:bb:1e:44:80:8e:a0:69:28:03:bd:04:87:5f:0c:86: + 80:63:db:47:ea:06:25:9e:7f:67:ef:16:77:37:02:8f:00:6a: + 59:cd:00:06:19:b0:43:34:8c:14:bf:29:fd:e0:8c:57:0f:63: + 7b:73:68:70:8a:13:ff:2d:39:a5:a7:69:fb:7b:13:3a:2c:09: + b5:c1:15:d8:7a:2f:77:33:16:7f:41:08:be:23:61:ac:aa:92: + 3f:38:40:06:87:3c:63:86:16:ba:4a:2d:ea:04:36:5a:fd:c7: + 80:8d +-----BEGIN CERTIFICATE----- +MIIDWTCCAsKgAwIBAgIBKzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjExMjMw +MjU1MDdaFw0xNzAxMjIwMjU1MDdaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT +ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu +Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyl2Gt3o6vPRN2GmM +Jb/R1zO3durZrrZ4FNbVymf99FwT0wG0vJLVoFH1/oFv2igzBwgg6SYnxqtDC3/O +fDvGnKRshSI9QIQnMtailO0UKUzv2azVo+p9R3YYV3wKSv66j7hERKVi6LDdWWvS +IGnxZOH20OWeiNoQ5VgY/IfOL2f2nfis2g8r9VgwBBMctXHOPSbHNANmOMqNEXXw +CxSrmLHczYHSaDOW1lBPpxnQIBVe4BiLB4MRLT1RFGhzzfJwxllQz+H1EojVcd4d +ki590YsJ/rQXvX5zB8ChavOvgDvk12JtHBWTkkclvfZQAj6cAH0VifI4EJXy7wn6 +tc+QYwIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug +KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG +SIb3DQEBBQUAA4GBAJL4BksyT/EiGJxn3CgDgu4UClmpB7seRICOoGkoA70Eh18M +hoBj20fqBiWef2fvFnc3Ao8AalnNAAYZsEM0jBS/Kf3gjFcPY3tzaHCKE/8tOaWn +aft7EzosCbXBFdh6L3czFn9BCL4jYayqkj84QAaHPGOGFrpKLeoENlr9x4CN +-----END CERTIFICATE----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_server.key b/examples/31_wpa2_enterprise/main/wpa2_server.key new file mode 100644 index 0000000000..e7d9d26200 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAyl2Gt3o6vPRN2GmMJb/R1zO3durZrrZ4FNbVymf99FwT0wG0 +vJLVoFH1/oFv2igzBwgg6SYnxqtDC3/OfDvGnKRshSI9QIQnMtailO0UKUzv2azV +o+p9R3YYV3wKSv66j7hERKVi6LDdWWvSIGnxZOH20OWeiNoQ5VgY/IfOL2f2nfis +2g8r9VgwBBMctXHOPSbHNANmOMqNEXXwCxSrmLHczYHSaDOW1lBPpxnQIBVe4BiL +B4MRLT1RFGhzzfJwxllQz+H1EojVcd4dki590YsJ/rQXvX5zB8ChavOvgDvk12Jt +HBWTkkclvfZQAj6cAH0VifI4EJXy7wn6tc+QYwIDAQABAoIBAHN/BZBaNoP5eyR0 +uQQigoUsgx1f1HWettJN+x7WU17P3pCjfJ/TvhEONjPXdBsyrkzpGr92g2UkAdPi +Udsd0rE8NqOVcxgLVMPzND+DP+qCerHlrtkqz+2lPSdRKB69W4UDShNTwSvFJdAP +dZICZT+Kp+953g2FR/ddXIVkjQ0IaKN8gD4j+JKpfVGMpKlYdUf8gK6Ag71VtUa5 +XzRDS4A9ytrpNcmaXaOhCVPdgCg20CULgpg6B6dG3XWzC16Erf8rMl0fsAB31K9a +qF87QS14JavdW1vdUFXCyqre9N58NUKxQjuhNADSB2sJEXkP2uhPcL+72Li8deRO +8MJgckECgYEA8ryoRI7bKn907Kx4Go7G4NS3hwuTd9jhhS8iQ1tatkQdqnBXnuWU +X20i02vYQGoZGsOl+DTgY2IWRYBPkGT+RwklJxCvYkZvOCgHaipoWsF+EOptv5Au +4ZERlBCWXzWdEgnx5nCFJQFfWBm77iC+muPpB+SryQmTld/RwvihxBsCgYEA1Wwp +Qq/5urpzz/uwNdMiMJ1lePi2HBvJeZOnw0LU+xUqXUkt6291CLhHNn8okPVeoNZP +iKP0Lid6IO2yp/3iCT5w9NNOMFlyhrVMAxYOkrM8AxlYnCwoCOoqN5x+4RrJLVFL +zrg+VN9vexfkOWdH9t8g+0gNn7MCX3adqy1/WVkCgYA4yXIEN/eGBbNw+xhN/kEA +sEMPUOH7E74OzmwRnfmm0mCuUwHspoEDoiCXnY9F4oxk+oiFfLlSBsx3DgGPIlFq +hPUwInMlZpz2Ykb5y1oGgWXgxzdNrYmKM8oM/aRwOba5VaJF6uT7N0r67WpN11NA +ITmPIywdKCRi163XExulKQKBgHyMgI/AbrbANPH9adofeuZwFFXCn1RMCwn+V3sm +N3DH609Bc6DgDKaoFDcDgkMGTtECAKw3Mjr1ItqwnQBYs169p+HYptqkeKeQiemL +J7oJC06rrgCF7F83eKe3lnv7y8e8l8bt0sJpGn/1c2TklyTFFlROulSmfQ4FBQJu +rNERAoGAec+0Wi5qYT917CPHqXcCUTg35kvtlLlgGdX6kNZRNszZUIF7O+wH4EJx +yxu3cgxZ2FL95Kf/oyOOnlOkRJ/clJbNBVSEHvJh64GL0PZ5V5szsscoGr6KY7SO +/kkJKC3OS/3fpto1/9yjJpoqJp9pzGU48PM0IKgd3ITQE6oOCCg= +-----END RSA PRIVATE KEY----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_server.pem b/examples/31_wpa2_enterprise/main/wpa2_server.pem new file mode 100644 index 0000000000..97d16aec59 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_server.pem @@ -0,0 +1,57 @@ +Bag Attributes + localKeyID: 4E 12 CF 3A FA D4 03 64 00 BB 98 1C 78 35 56 4A AC C3 1E 17 +subject=/C=FR/ST=Radius/O=Example Inc./CN=Example Server Certificate/emailAddress=admin@example.com +issuer=/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.com/CN=Example Certificate Authority +-----BEGIN CERTIFICATE----- +MIIDWTCCAsKgAwIBAgIBKzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjExMjMw +MjU1MDdaFw0xNzAxMjIwMjU1MDdaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT +ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu +Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyl2Gt3o6vPRN2GmM +Jb/R1zO3durZrrZ4FNbVymf99FwT0wG0vJLVoFH1/oFv2igzBwgg6SYnxqtDC3/O +fDvGnKRshSI9QIQnMtailO0UKUzv2azVo+p9R3YYV3wKSv66j7hERKVi6LDdWWvS +IGnxZOH20OWeiNoQ5VgY/IfOL2f2nfis2g8r9VgwBBMctXHOPSbHNANmOMqNEXXw +CxSrmLHczYHSaDOW1lBPpxnQIBVe4BiLB4MRLT1RFGhzzfJwxllQz+H1EojVcd4d +ki590YsJ/rQXvX5zB8ChavOvgDvk12JtHBWTkkclvfZQAj6cAH0VifI4EJXy7wn6 +tc+QYwIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug +KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG +SIb3DQEBBQUAA4GBAJL4BksyT/EiGJxn3CgDgu4UClmpB7seRICOoGkoA70Eh18M +hoBj20fqBiWef2fvFnc3Ao8AalnNAAYZsEM0jBS/Kf3gjFcPY3tzaHCKE/8tOaWn +aft7EzosCbXBFdh6L3czFn9BCL4jYayqkj84QAaHPGOGFrpKLeoENlr9x4CN +-----END CERTIFICATE----- +Bag Attributes + localKeyID: 4E 12 CF 3A FA D4 03 64 00 BB 98 1C 78 35 56 4A AC C3 1E 17 +Key Attributes: +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIxxT4lUY0dvgCAggA +MBQGCCqGSIb3DQMHBAiQ8/bzpj1InASCBMjU0Nl0/CMHnNAWZ6t1yb93vHJUHHsv +0acQNqeN8ToS1Wz0delbNnJMZ5lkO3ei542d2HwbGW4KYgWuMBPh2qgpdnnUfBPE +C6iCtGCDugVgZl3W7VPjQzMjsExuwYaZf7dhBkQEbuzuGXDrsZL3sauAeOla8V34 +yYESS9P7Jyd0prMgDG5yap9acorjiSLpqHsCogp/vlERSl8f/+yxY5dJuLo+54Z6 +Q8GjlpoiEUijy/Gs/BKcyTX3zddJm/yA3PF0JM8ZSH9K0pBa3l2VJYdizWGl3G59 +uozlMVZrg/KxDgfoe7QGit/WUGiq4fYXIUVKSKOfRmpD8YaTaUkGtoao9VjUYXul +01yU3DSRct9O/r1MG9QQjmYSi05t+Cohp/FNd0WqLlKPilDK86Wu4xECjOI+KsOe +kccUDBuaTMxdoYsq5Ge+V79YR1yABYtgkGymR9mS357Za9IlXxExhDBb2ky0MMlx +DfgimcWOlc4IWGrhheezZaTjgUO/Q0izrxD/ZBYNUmSnYDpRj4mk/sxQ6aDytlEu +ZSnTo+jbyXTh8nVwxhOB2PdWNmFOkRcGOJci8MErd8ArdWniw68MoYwhHTabDpwo +0nEs1MhqoiMAgn7iluN+cscV7pz4n5zriZX3Rw1ivcUPQ8RR6y5h/nR9Du5fCyRm +v5vF01w+o+XejU54DyqRptWiR6yHDJer2TIUqTjKt4NQENZyXEMh+ls6arjZ0mRp +i8rv3M/Z8NLcTQ7lD/gAGTWxzsNIfMcBQX9X9R6V4BmzhmQQA+/pbLh2IOOVPXO3 +sUGXChyQSa5xE1VpVle28Q3GqKq03W0W+8EtGy889px9/MGWLfCMrJNCv4zH7d/j +FTRkiNdtBZrmWTyUgBVgQoTVPxyzBBC11HXzRGxiEQft/NFwd0dzKoNfNsgGGa0Q +AzKmPJselaJdqol961z3RbzEpfyqvSnMbui/iIMV2lTEc/EQWoaQ3SGHf3qdhlQ5 +MScGocq+hskrMmgW1XVG3HDowdC5K5sKXUYJmixNGHWXFao/FZUAVBmQD+290Qkl +EMKQ2xw3PJ2tk47EpdscixayDpiOJQEdgys3oA1W06bIpxNaERYObqp8M62fet9I +wuRZyNWuKVyyilyB9sTjmgD55BXt6B+jkmkwHq15RA85AbsHHttugyMA//V+oiYt +B0BmSKpzSr6nEnr+0NiWybWlN97yLHUrtFiLMyKb6+qAMbxdsET6vfWsASuC21Lq +ZHgW3EofIEDz7r59w4NHwCLGBttpgKLkmrvjt17cMdmsEXbWFgw+9pi/uqEaRaq5 +/ekb9uPzbS7o4ArGQ+WBFUA6ONV98i2ZMOmfvq/dgBAQcRLlI7Cp+yikcMbzJ3Aq +VJc53y0Gl8awDXMbOiH0l1ij+3mQ5xPZbuv2ofcg+4enoK1cclG6ryWWGPMDcAg2 +JivgEzn6eFsiPRnlidrJUy6zJYxCsjSPodcbFH7DXnwPA8+C9P1it2bnqPdQWzXA +JiUFtvmRgEVmOAVCbZLkNPa+K0K8Ymzu3ZYchVMduxJh1xNKId+FM2BGOmlYqDUJ +jnGqHciOxd+0crjaPd0isxUgS7bTd3XdQEIkT/yESS9aJEHsnBFkEXsXSDpxpf7a +dE4= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/examples/33_gatt_server_service_table/Makefile b/examples/33_gatt_server_service_table/Makefile new file mode 100644 index 0000000000..2ff9c6eaee --- /dev/null +++ b/examples/33_gatt_server_service_table/Makefile @@ -0,0 +1,11 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := gatt_server_service_table_demo + +COMPONENT_ADD_INCLUDEDIRS := components/include + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/33_gatt_server_service_table/README.rst b/examples/33_gatt_server_service_table/README.rst new file mode 100644 index 0000000000..7a763998d7 --- /dev/null +++ b/examples/33_gatt_server_service_table/README.rst @@ -0,0 +1,10 @@ +ESP-IDF GATT SERVER create attribute table demo +=============================================== + +This is the demo for user to use ESP_APIs to create a GATT Server attribute table. +The table is easy to use to create GATT server service database without use each "attribute create" functions. +Actually, there are two way to create server service and characteristics. +One is use the esp_gatts_create_service or esp_ble_gatts_add_char and etc. +The other way is use esp_ble_gatts_create_attr_tab. +The important things: the two ways cannot use in the same service, but can use in different service. + diff --git a/examples/12_blufi/components/blufi/component.mk b/examples/33_gatt_server_service_table/main/component.mk similarity index 71% rename from examples/12_blufi/components/blufi/component.mk rename to examples/33_gatt_server_service_table/main/component.mk index 297e63f919..79edf031d3 100644 --- a/examples/12_blufi/components/blufi/component.mk +++ b/examples/33_gatt_server_service_table/main/component.mk @@ -6,8 +6,3 @@ # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, # please read the ESP-IDF documents if you need to do this. # - -COMPONENT_SRCDIRS := . - -CFLAGS += -Wno-error=unused-label -Wno-error=return-type -Wno-error=missing-braces -Wno-error=pointer-sign -Wno-error=parentheses -I./include - diff --git a/examples/33_gatt_server_service_table/main/gatts_table_creat_demo.c b/examples/33_gatt_server_service_table/main/gatts_table_creat_demo.c new file mode 100644 index 0000000000..024362c5b7 --- /dev/null +++ b/examples/33_gatt_server_service_table/main/gatts_table_creat_demo.c @@ -0,0 +1,326 @@ +// Copyright 2015-2016 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. + + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "bt.h" +#include "bta_api.h" + +#include "esp_gap_ble_api.h" +#include "esp_gatts_api.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" +#include "esp_bt_main.h" +#include "gatts_table_creat_demo.h" + + +#define HEART_PROFILE_NUM 1 +#define HEART_PROFILE_APP_IDX 0 +#define ESP_HEART_RATE_APP_ID 0x55 +#define SAMPLE_DEVICE_NAME "ESP_HEART_RATE" +#define SAMPLE_MANUFACTURER_DATA_LEN 17 +#define HEART_RATE_SVC_INST_ID 0 + +#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 + +uint8_t char1_str[] ={0x11,0x22,0x33}; + +uint16_t heart_rate_handle_table[HRS_IDX_NB]; + +esp_attr_value_t gatts_demo_char1_val = +{ + .attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX, + .attr_len = sizeof(char1_str), + .attr_value = char1_str, +}; + + +static uint8_t heart_rate_service_uuid[16] = { + /* LSB <--------------------------------------------------------------------------------> MSB */ + //first uuid, 16bit, [12],[13] is the value + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x18, 0x0D, 0x00, 0x00, +}; + + +static esp_ble_adv_data_t heart_rate_adv_config = { + .set_scan_rsp = false, + .include_name = true, + .include_txpower = true, + .min_interval = 0x20, + .max_interval = 0x40, + .appearance = 0x00, + .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN, + .p_manufacturer_data = NULL, //&test_manufacturer[0], + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = 32, + .p_service_uuid = heart_rate_service_uuid, + .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), +}; + +static esp_ble_adv_params_t heart_rate_adv_params = { + .adv_int_min = 0x20, + .adv_int_max = 0x40, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + //.peer_addr = + //.peer_addr_type = + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, +}; + +struct gatts_profile_inst { + esp_gatts_cb_t gatts_cb; + uint16_t gatts_if; + uint16_t app_id; + uint16_t conn_id; + uint16_t service_handle; + esp_gatt_srvc_id_t service_id; + uint16_t char_handle; + esp_bt_uuid_t char_uuid; + esp_gatt_perm_t perm; + esp_gatt_char_prop_t property; + uint16_t descr_handle; + esp_bt_uuid_t descr_uuid; +}; + +static void gatts_profile_event_handler(esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + +/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ +static struct gatts_profile_inst heart_rate_profile_tab[HEART_PROFILE_NUM] = { + [HEART_PROFILE_APP_IDX] = { + .gatts_cb = gatts_profile_event_handler, + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, + +}; + +/* + * HTPT PROFILE ATTRIBUTES + **************************************************************************************** + */ + + +/* + * Heart Rate PROFILE ATTRIBUTES + **************************************************************************************** + */ + +/// Heart Rate Sensor Service +static const uint16_t heart_rate_svc = ESP_GATT_UUID_HEART_RATE_SVC; + +#define CHAR_DECLARATION_SIZE (sizeof(uint8_t)) +static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE; +static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE; +static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; +static const uint8_t char_prop_notify = ESP_GATT_CHAR_PROP_BIT_NOTIFY; +static const uint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ; +static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE|ESP_GATT_CHAR_PROP_BIT_READ; + +/// Heart Rate Sensor Service - Heart Rate Measurement Characteristic, notify +static const uint16_t heart_rate_meas_uuid = ESP_GATT_HEART_RATE_MEAS; +static const uint8_t heart_measurement_ccc[2] ={ 0x00, 0x00}; + + +/// Heart Rate Sensor Service -Body Sensor Location characteristic, read +static const uint16_t body_sensor_location_uuid = ESP_GATT_BODY_SENSOR_LOCATION; +static const uint8_t body_sensor_loc_val[1] = {0x00}; + + +/// Heart Rate Sensor Service - Heart Rate Control Point characteristic, write&read +static const uint16_t heart_rate_ctrl_point = ESP_GATT_HEART_RATE_CNTL_POINT; +static const uint8_t heart_ctrl_point[1] = {0x00}; + +/// Full HRS Database Description - Used to add attributes into the database +static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] = +{ + // Heart Rate Service Declaration + [HRS_IDX_SVC] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, + sizeof(uint16_t), sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}}, + + // Heart Rate Measurement Characteristic Declaration + [HRS_IDX_HR_MEAS_CHAR] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, + CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_notify}}, + + // Heart Rate Measurement Characteristic Value + [HRS_IDX_HR_MEAS_VAL] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ, + HRPS_HT_MEAS_MAX_LEN,0, NULL}}, + + // Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor + [HRS_IDX_HR_MEAS_NTF_CFG] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, + sizeof(uint16_t),sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}}, + + // Body Sensor Location Characteristic Declaration + [HRS_IDX_BOBY_SENSOR_LOC_CHAR] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, + CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read}}, + + // Body Sensor Location Characteristic Value + [HRS_IDX_BOBY_SENSOR_LOC_VAL] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&body_sensor_location_uuid, ESP_GATT_PERM_READ, + sizeof(uint8_t), sizeof(body_sensor_loc_val), (uint8_t *)body_sensor_loc_val}}, + + // Heart Rate Control Point Characteristic Declaration + [HRS_IDX_HR_CTNL_PT_CHAR] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, + CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}}, + + // Heart Rate Control Point Characteristic Value + [HRS_IDX_HR_CTNL_PT_VAL] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_ctrl_point, ESP_GATT_PERM_WRITE|ESP_GATT_PERM_READ, + sizeof(uint8_t), sizeof(heart_ctrl_point), (uint8_t *)heart_ctrl_point}}, +}; + + + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + LOG_ERROR("GAP_EVT, event %d\n", event); + + switch (event) { + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + esp_ble_gap_start_advertising(&heart_rate_adv_params); + break; + default: + break; + } +} + +static void gatts_profile_event_handler(esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + LOG_ERROR("event = %x\n",event); + switch (event) { + case ESP_GATTS_REG_EVT: + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME); + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gap_config_adv_data(&heart_rate_adv_config); + + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gatts_create_attr_tab(heart_rate_gatt_db, gatts_if, + HRS_IDX_NB, HEART_RATE_SVC_INST_ID); + break; + case ESP_GATTS_READ_EVT: + + break; + case ESP_GATTS_WRITE_EVT: + break; + case ESP_GATTS_EXEC_WRITE_EVT: + break; + case ESP_GATTS_MTU_EVT: + break; + case ESP_GATTS_CONF_EVT: + break; + case ESP_GATTS_UNREG_EVT: + break; + case ESP_GATTS_DELETE_EVT: + break; + case ESP_GATTS_START_EVT: + break; + case ESP_GATTS_STOP_EVT: + break; + case ESP_GATTS_CONNECT_EVT: + break; + case ESP_GATTS_DISCONNECT_EVT: + break; + case ESP_GATTS_OPEN_EVT: + break; + case ESP_GATTS_CANCEL_OPEN_EVT: + break; + case ESP_GATTS_CLOSE_EVT: + break; + case ESP_GATTS_LISTEN_EVT: + break; + case ESP_GATTS_CONGEST_EVT: + break; + case ESP_GATTS_CREAT_ATTR_TAB_EVT:{ + LOG_ERROR("The number handle =%x\n",param->add_attr_tab.num_handle); + if(param->add_attr_tab.num_handle == HRS_IDX_NB){ + memcpy(heart_rate_handle_table, param->add_attr_tab.handles, + sizeof(heart_rate_handle_table)); + esp_ble_gatts_start_service(heart_rate_handle_table[HRS_IDX_SVC]); + } + + break; + } + + default: + break; + } +} + + +static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t *param) +{ + LOG_INFO("EVT %d, gatts if %d\n", event, gatts_if); + + /* If event is register event, store the gatts_if for each profile */ + if (event == ESP_GATTS_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + heart_rate_profile_tab[HEART_PROFILE_APP_IDX].gatts_if = gatts_if; + } else { + LOG_INFO("Reg app failed, app_id %04x, status %d\n", + param->reg.app_id, + param->reg.status); + return; + } + } + + do { + int idx; + for (idx = 0; idx < HEART_PROFILE_NUM; idx++) { + if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gatts_if == heart_rate_profile_tab[idx].gatts_if) { + if (heart_rate_profile_tab[idx].gatts_cb) { + heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param); + } + } + } + } while (0); +} + +void app_main() +{ + esp_err_t ret; + + esp_bt_controller_init(); + LOG_INFO("%s init bluetooth\n", __func__); + ret = esp_bluedroid_init(); + if (ret) { + LOG_ERROR("%s init bluetooth failed\n", __func__); + return; + } + ret = esp_bluedroid_enable(); + if (ret) { + LOG_ERROR("%s enable bluetooth failed\n", __func__); + return; + } + + esp_ble_gatts_register_callback(gatts_event_handler); + esp_ble_gap_register_callback(gap_event_handler); + esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID); + return; +} diff --git a/examples/33_gatt_server_service_table/main/gatts_table_creat_demo.h b/examples/33_gatt_server_service_table/main/gatts_table_creat_demo.h new file mode 100644 index 0000000000..c12d1aa59b --- /dev/null +++ b/examples/33_gatt_server_service_table/main/gatts_table_creat_demo.h @@ -0,0 +1,48 @@ +// Copyright 2015-2016 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. + + +#include +#include +#include + +/* + * DEFINES + **************************************************************************************** + */ + +#define HRPS_HT_MEAS_MAX_LEN (13) + +#define HRPS_MANDATORY_MASK (0x0F) +#define HRPS_BODY_SENSOR_LOC_MASK (0x30) +#define HRPS_HR_CTNL_PT_MASK (0xC0) + + +///Attributes State Machine +enum +{ + HRS_IDX_SVC, + + HRS_IDX_HR_MEAS_CHAR, + HRS_IDX_HR_MEAS_VAL, + HRS_IDX_HR_MEAS_NTF_CFG, + + HRS_IDX_BOBY_SENSOR_LOC_CHAR, + HRS_IDX_BOBY_SENSOR_LOC_VAL, + + HRS_IDX_HR_CTNL_PT_CHAR, + HRS_IDX_HR_CTNL_PT_VAL, + + HRS_IDX_NB, +}; diff --git a/examples/33_gatt_server_service_table/sdkconfig.defaults b/examples/33_gatt_server_service_table/sdkconfig.defaults new file mode 100644 index 0000000000..e435f383c8 --- /dev/null +++ b/examples/33_gatt_server_service_table/sdkconfig.defaults @@ -0,0 +1,14 @@ +# Override some defaults so BT stack is enabled +# in this example + +# +# BT config +# +CONFIG_BT_ENABLED=y + +# +# ESP32-specific config +# +CONFIG_ESP32_ENABLE_STACK_BT=y +# CONFIG_ESP32_ENABLE_STACK_NONE is not set +CONFIG_MEMMAP_BT=y diff --git a/make/build_examples.sh b/make/build_examples.sh index f1b37b7b4b..ab59208795 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -11,11 +11,10 @@ EXAMPLE_NUM=1 RESULT=0 +FAILED_EXAMPLES="" RESULT_WARNINGS=22 # magic number result code for "warnings found" -set -e - for example in ${IDF_PATH}/examples/*; do [ -f ${example}/Makefile ] || continue echo "Building ${example} as ${EXAMPLE_NUM}..." @@ -30,16 +29,17 @@ for example in ${IDF_PATH}/examples/*; do # build non-verbose first BUILDLOG=$(mktemp -t examplebuild.XXXX.log) ( + set -o pipefail # so result of make all isn't lost when piping to tee set -e make clean defconfig - make all 2>&1 | tee $BUILDLOG - ) || (RESULT=$?; make V=1) # only build verbose if there's an error + make $* all 2>&1 | tee $BUILDLOG + ) || { RESULT=$?; FAILED_EXAMPLES+=" ${example}"; make V=1; } # only build verbose if there's an error popd EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) - if [ $RESULT -eq 0 ] && grep -q ": warning:" $BUILDLOG; then - echo "Build will fail, due to warnings in this example" - RESULT=$RESULT_WARNINGS + if grep -q ": warning:" $BUILDLOG; then + [ $RESULT -eq 0 ] && RESULT=$RESULT_WARNINGS + FAILED_EXAMPLES+=" ${example} (warnings)" fi rm -f $BUILDLOG @@ -49,5 +49,7 @@ if [ $RESULT -eq $RESULT_WARNINGS ]; then echo "Build would have passed, except for warnings." fi +[ $RESULT -eq 0 ] || echo "Failed examples: $FAILED_EXAMPLES" + exit $RESULT diff --git a/make/project.mk b/make/project.mk index a5e487b85c..c4c3022a6b 100644 --- a/make/project.mk +++ b/make/project.mk @@ -30,6 +30,7 @@ help: @echo "make clean - Remove all build output" @echo "make size - Display the memory footprint of the app" @echo "make erase_flash - Erase entire flash contents" + @echo "make monitor - Display serial output on terminal console" @echo "" @echo "make app - Build just the app" @echo "make app-flash - Flash just the app" @@ -175,6 +176,10 @@ else endif @echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) + +# Git version of ESP-IDF (of the form v1.0-285-g5c4f707) +IDF_VER := $(shell git -C $(IDF_PATH) describe) + # Set default LDFLAGS LDFLAGS ?= -nostdlib \ @@ -186,6 +191,7 @@ LDFLAGS ?= -nostdlib \ -Wl,--start-group \ $(COMPONENT_LDFLAGS) \ -lgcc \ + -lstdc++ \ -Wl,--end-group \ -Wl,-EL @@ -199,7 +205,7 @@ LDFLAGS ?= -nostdlib \ # CPPFLAGS used by C preprocessor # If any flags are defined in application Makefile, add them at the end. -CPPFLAGS := -DESP_PLATFORM -MMD -MP $(CPPFLAGS) $(EXTRA_CPPFLAGS) +CPPFLAGS := -DESP_PLATFORM -D IDF_VER=\"$(IDF_VER)\" -MMD -MP $(CPPFLAGS) $(EXTRA_CPPFLAGS) # Warnings-related flags relevant both for C and C++ COMMON_WARNING_FLAGS = -Wall -Werror=all \ @@ -415,3 +421,36 @@ endef # filter/subst in expression ensures all submodule paths begin with $(IDF_PATH), and then strips that prefix # so the argument is suitable for use with 'git submodule' commands $(foreach submodule,$(subst $(IDF_PATH)/,,$(filter $(IDF_PATH)/%,$(COMPONENT_SUBMODULES))),$(eval $(call GenerateSubmoduleCheckTarget,$(submodule)))) + + +# Check toolchain version using the output of xtensa-esp32-elf-gcc --version command. +# The output normally looks as follows +# xtensa-esp32-elf-gcc (crosstool-NG crosstool-ng-1.22.0-59-ga194053) 4.8.5 +# The part in brackets is extracted into TOOLCHAIN_COMMIT_DESC variable, +# the part after the brackets is extracted into TOOLCHAIN_GCC_VER. +ifdef CONFIG_TOOLPREFIX +ifndef MAKE_RESTARTS +TOOLCHAIN_COMMIT_DESC := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc.*?\ \(([^)]*).*|\1|gp') +TOOLCHAIN_GCC_VER := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc.*?\ \(.*\)\ (.*)|\1|gp') + +# Officially supported version(s) +SUPPORTED_TOOLCHAIN_COMMIT_DESC := crosstool-NG crosstool-ng-1.22.0-61-gab8375a +SUPPORTED_TOOLCHAIN_GCC_VERSIONS := 5.2.0 + +ifdef TOOLCHAIN_COMMIT_DESC +ifneq ($(TOOLCHAIN_COMMIT_DESC), $(SUPPORTED_TOOLCHAIN_COMMIT_DESC)) +$(info WARNING: Toolchain version is not supported: $(TOOLCHAIN_COMMIT_DESC)) +$(info Expected to see version: $(SUPPORTED_TOOLCHAIN_COMMIT_DESC)) +$(info Please check ESP-IDF setup instructions and update the toolchain, or proceed at your own risk.) +endif +ifeq (,$(findstring $(TOOLCHAIN_GCC_VER), $(SUPPORTED_TOOLCHAIN_GCC_VERSIONS))) +$(info WARNING: Compiler version is not supported: $(TOOLCHAIN_GCC_VER)) +$(info Expected to see version(s): $(SUPPORTED_TOOLCHAIN_GCC_VERSIONS)) +$(info Please check ESP-IDF setup instructions and update the toolchain, or proceed at your own risk.) +endif +else +$(info WARNING: Failed to find Xtensa toolchain, may need to alter PATH or set one in the configuration menu) +endif # TOOLCHAIN_COMMIT_DESC + +endif #MAKE_RESTARTS +endif #CONFIG_TOOLPREFIX diff --git a/make/project_config.mk b/make/project_config.mk index 187d1ac282..011aa1ff0e 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -11,6 +11,10 @@ KCONFIG_TOOL_DIR=$(IDF_PATH)/tools/kconfig # unless it's overriden (happens for bootloader) SDKCONFIG ?= $(PROJECT_PATH)/sdkconfig +# SDKCONFIG_DEFAULTS is an optional file containing default +# overrides (usually used for esp-idf examples) +SDKCONFIG_DEFAULTS ?= $(PROJECT_PATH)/sdkconfig.defaults + # reset MAKEFLAGS as the menuconfig makefile uses implicit compile rules $(KCONFIG_TOOL_DIR)/mconf $(KCONFIG_TOOL_DIR)/conf: MAKEFLAGS=$(ORIGINAL_MAKEFLAGS) CC=$(HOSTCC) LD=$(HOSTLD) \ @@ -21,21 +25,26 @@ KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(abspath $(BUILD_DIR_BASE)/include/sdkconfi COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" KCONFIG_CONFIG=$(SDKCONFIG) \ COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" -menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig +menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(call prereq_if_explicit,defconfig) $(summary) MENUCONFIG $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig ifeq ("$(wildcard $(SDKCONFIG))","") ifeq ("$(call prereq_if_explicit,defconfig)","") -# if not configuration is present and defconfig is not a target, run makeconfig -$(SDKCONFIG): menuconfig +# if not configuration is present and defconfig is not a target, run defconfig then menuconfig +$(SDKCONFIG): defconfig menuconfig else +# otherwise, just defconfig $(SDKCONFIG): defconfig endif endif +# defconfig creates a default config, based on SDKCONFIG_DEFAULTS if present defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) $(summary) DEFCONFIG +ifneq ("$(wildcard $(SDKCONFIG_DEFAULTS))","") + cat $(SDKCONFIG_DEFAULTS) >> $(SDKCONFIG) # append defaults to sdkconfig, will override existing values +endif mkdir -p $(BUILD_DIR_BASE)/include/config $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --olddefconfig $(IDF_PATH)/Kconfig diff --git a/tools/unit-test-app/components/unity/include/unity_config.h b/tools/unit-test-app/components/unity/include/unity_config.h index b8e56a79c1..07df5b3058 100644 --- a/tools/unit-test-app/components/unity/include/unity_config.h +++ b/tools/unit-test-app/components/unity/include/unity_config.h @@ -61,11 +61,20 @@ void unity_run_all_tests(); .desc = desc_, \ .fn = &UNITY_TEST_UID(test_func_), \ .file = __FILE__, \ - .line = __LINE__ \ + .line = __LINE__, \ + .next = NULL \ }; \ unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \ }\ static void UNITY_TEST_UID(test_func_) (void) +/** + * Note: initialization of test_desc_t fields above has to be done exactly + * in the same order as the fields are declared in the structure. + * Otherwise the initializer will not be valid in C++ (which doesn't + * support designated initializers). G++ can parse the syntax, but + * field names are treated as annotations and don't affect initialization + * order. Also make sure all the fields are initialized. + */ // shorthand to check esp_err_t return code #define TEST_ESP_OK(rc) TEST_ASSERT_EQUAL_INT32(ESP_OK, rc)