From 4fc14e8eb67130b03a8acaeab8a4cee432991d73 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Tue, 19 Feb 2019 22:50:18 +0300 Subject: [PATCH 1/2] vfs: Adds semihosting VFS driver --- components/vfs/CMakeLists.txt | 3 +- components/vfs/Kconfig | 16 ++ components/vfs/include/esp_vfs_semihost.h | 46 +++++ components/vfs/vfs_semihost.c | 216 ++++++++++++++++++++++ docs/Doxyfile | 1 + 5 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 components/vfs/include/esp_vfs_semihost.h create mode 100644 components/vfs/vfs_semihost.c diff --git a/components/vfs/CMakeLists.txt b/components/vfs/CMakeLists.txt index 77083dc20..6089473ee 100644 --- a/components/vfs/CMakeLists.txt +++ b/components/vfs/CMakeLists.txt @@ -1,5 +1,6 @@ set(COMPONENT_SRCS "vfs.c" - "vfs_uart.c") + "vfs_uart.c" + "vfs_semihost.c") set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_REQUIRES) diff --git a/components/vfs/Kconfig b/components/vfs/Kconfig index c479a989e..b34679b20 100644 --- a/components/vfs/Kconfig +++ b/components/vfs/Kconfig @@ -15,4 +15,20 @@ menu "Virtual file system" help Disabling this option can save memory when the support for termios.h is not required. + menu "Host File System I/O (Semihosting)" + config SEMIHOSTFS_MAX_MOUNT_POINTS + int "Maximum number of the host filesystem mount points" + default 1 + help + Define maximum number of host filesystem mount points. + + config SEMIHOSTFS_HOST_PATH_MAX_LEN + int "Maximum path length for the host base directory" + default 128 + help + Define maximum path length for the host base directory which is to be mounted. + If host path passed to esp_vfs_semihost_register() is longer than this value + it will be truncated. + endmenu + endmenu diff --git a/components/vfs/include/esp_vfs_semihost.h b/components/vfs/include/esp_vfs_semihost.h new file mode 100644 index 000000000..20a0ed550 --- /dev/null +++ b/components/vfs/include/esp_vfs_semihost.h @@ -0,0 +1,46 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "esp_vfs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief add virtual filesystem semihosting driver + * + * @param base_path VFS path to mount host directory + * @param host_path host path to mount; if NULL default dirctory will be used (see OpenOCD configuration) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if esp_vfs_semihost_register was already called for specified VFS path + * - ESP_ERR_NO_MEM if there are no slots to register new mount point + */ +esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path); + +/** + * @brief Un-register semihosting driver from VFS + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if semihosting driver is not registered in VFS at that path + */ +esp_err_t esp_vfs_semihost_unregister(const char* base_path); + +#ifdef __cplusplus +} +#endif diff --git a/components/vfs/vfs_semihost.c b/components/vfs/vfs_semihost.c new file mode 100644 index 000000000..d97fa9188 --- /dev/null +++ b/components/vfs/vfs_semihost.c @@ -0,0 +1,216 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include "soc/cpu.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_vfs.h" + +#define LOG_LOCAL_LEVEL ESP_LOG_NONE +#include "esp_log.h" +const static char *TAG = "esp_semihost"; + +#define SYSCALL_INSTR "break 1,1\n" +#define SYS_OPEN 0x01 +#define SYS_CLOSE 0x02 +#define SYS_WRITE 0x05 +#define SYS_READ 0x06 +#define SYS_SEEK 0x0A + +/** ESP-specific file open flag. Indicates that path passed to open() is absolute host path. */ +#define ESP_O_SEMIHOST_ABSPATH 0x80000000 + +typedef struct { + char base_path[ESP_VFS_PATH_MAX+1]; /* base path in VFS where host semohosting dir is mounted */ + char host_path[CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN+1]; /* host path to use as base dir for open files */ +} vfs_semihost_ctx_t; + +static vfs_semihost_ctx_t s_semhost_ctx[CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS]; + + +static inline int generic_syscall(int sys_nr, int arg1, int arg2, int arg3, int arg4, int* ret_errno) +{ + int host_ret, host_errno; + + if (!esp_cpu_in_ocd_debug_mode()) { + *ret_errno = EIO; + return -1; + } + __asm__ volatile ( + "mov a2, %[sys_nr]\n" \ + "mov a3, %[arg1]\n" \ + "mov a4, %[arg2]\n" \ + "mov a5, %[arg3]\n" \ + "mov a6, %[arg4]\n" \ + SYSCALL_INSTR \ + "mov %[host_ret], a2\n" \ + "mov %[host_errno], a3\n" \ + :[host_ret]"=r"(host_ret),[host_errno]"=r"(host_errno) + :[sys_nr]"r"(sys_nr),[arg1]"r"(arg1),[arg2]"r"(arg2),[arg3]"r"(arg3),[arg4]"r"(arg4) + :"a2","a3","a4","a5","a6"); + *ret_errno = host_errno; + return host_ret; +} + +inline bool ctx_is_unused(const vfs_semihost_ctx_t* ctx) +{ + return ctx->base_path[0] == 0; +} + +inline bool ctx_uses_abspath(const vfs_semihost_ctx_t* ctx) +{ + return ctx->host_path[0]; +} + +static int vfs_semihost_open(void* ctx, const char * path, int flags, int mode) +{ + int fd = -1, host_err = 0; + char *host_path; + vfs_semihost_ctx_t *semi_ctx = ctx; + + ESP_LOGV(TAG, "%s: %p '%s 0x%x 0x%x'", __func__, semi_ctx, path, flags, mode); + if (ctx_uses_abspath(semi_ctx)) { + flags |= ESP_O_SEMIHOST_ABSPATH; + host_path = malloc(strlen(semi_ctx->host_path)+strlen(path)+1); + if(host_path == NULL) { + errno = ENOMEM; + return -1; + } + strcpy(host_path, semi_ctx->host_path); + strcat(host_path, path); + } else { + host_path = (char *)path; + } + fd = generic_syscall(SYS_OPEN, (int)host_path, strlen(host_path), flags, mode, &host_err); + if (ctx_uses_abspath(semi_ctx)) { + free(host_path); + } + if (fd == -1) { + errno = host_err; + } + return fd; +} + +static ssize_t vfs_semihost_write(void* ctx, int fd, const void * data, size_t size) +{ + int host_err = 0; + size_t ret = -1; + + ESP_LOGV(TAG, "%s: %d %u bytes", __func__, fd, size); + ret = generic_syscall(SYS_WRITE, fd, (int)data, size, 0, &host_err); + if (ret == -1) { + errno = host_err; + } + return (ssize_t)ret; +} + +static ssize_t vfs_semihost_read(void* ctx, int fd, void* data, size_t size) +{ + int host_err = 0; + size_t ret = -1; + + ESP_LOGV(TAG, "%s: %d %u bytes", __func__, fd, size); + ret = generic_syscall(SYS_READ, fd, (int)data, size, 0, &host_err); + if (ret == -1) { + errno = host_err; + } + return (ssize_t)ret; +} + +static int vfs_semihost_close(void* ctx, int fd) +{ + int ret = -1, host_err = 0; + + ESP_LOGV(TAG, "%s: %d", __func__, fd); + ret = generic_syscall(SYS_CLOSE, fd, 0, 0, 0, &host_err); + if (ret == -1) { + errno = host_err; + } + return ret; +} + +static off_t vfs_semihost_lseek(void* ctx, int fd, off_t size, int mode) +{ + int ret = -1, host_err = 0; + + ESP_LOGV(TAG, "%s: %d %ld %d", __func__, fd, size, mode); + ret = generic_syscall(SYS_SEEK, fd, size, mode, 0, &host_err); + if (ret == -1) { + errno = host_err; + } + return (off_t)ret; +} + +esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path) +{ + const esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_CONTEXT_PTR, + .write_p = &vfs_semihost_write, + .open_p = &vfs_semihost_open, + .close_p = &vfs_semihost_close, + .read_p = &vfs_semihost_read, + .lseek_p = &vfs_semihost_lseek, + }; + ESP_LOGD(TAG, "Register semihosting driver '%s' -> '%s'", base_path, host_path ? host_path : "null"); + if (!esp_cpu_in_ocd_debug_mode()) { + ESP_LOGE(TAG, "OpenOCD is not connected!"); + return ESP_ERR_NOT_SUPPORTED; + } + int i = 0; + for (i = 0; i < CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS; i++) { + if (ctx_is_unused(&s_semhost_ctx[i])) { + break; + } + if (strcmp(base_path, s_semhost_ctx[i].base_path) == 0) { + return ESP_ERR_INVALID_STATE; + } + } + if (i == CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS) { + return ESP_ERR_NO_MEM; + } + strlcpy(s_semhost_ctx[i].base_path, base_path, sizeof(s_semhost_ctx[i].base_path) - 1); + if (host_path) { + strlcpy(s_semhost_ctx[i].host_path, host_path, sizeof(s_semhost_ctx[i].host_path) - 1); + } + ESP_LOGD(TAG, "Register semihosting driver %d %p", i, &s_semhost_ctx[i]); + return esp_vfs_register(base_path, &vfs, &s_semhost_ctx[i]); +} + +esp_err_t esp_vfs_semihost_unregister(const char* base_path) +{ + ESP_LOGD(TAG, "Unregister semihosting driver @ '%s'", base_path); + int i = 0; + for (i = 0; i < CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS; i++) { + if (s_semhost_ctx[i].base_path[0] != 0 && strcmp(base_path, s_semhost_ctx[i].base_path) == 0) { + break; + } + } + if (i == CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS) { + return ESP_ERR_INVALID_ARG; + } + esp_err_t ret = esp_vfs_unregister(s_semhost_ctx[i].base_path); + if (ret != ESP_OK) { + return ret; + } + s_semhost_ctx[i].base_path[0] = 0; + s_semhost_ctx[i].host_path[0] = 0; + ESP_LOGD(TAG, "Unregistered semihosting driver @ '%s'", base_path); + return ESP_OK; +} diff --git a/docs/Doxyfile b/docs/Doxyfile index b79aa3e2c..b88c6fc78 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -137,6 +137,7 @@ INPUT = \ ## Virtual Filesystem ../../components/vfs/include/esp_vfs.h \ ../../components/vfs/include/esp_vfs_dev.h \ + ../../components/vfs/include/esp_vfs_semihost.h \ ## FAT Filesystem ## NOTE: for two lines below header_file.inc is not used ../../components/fatfs/src/esp_vfs_fat.h \ From b5ac319160c43c54113b52414db26f2a7fe6c477 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Tue, 19 Feb 2019 22:57:03 +0300 Subject: [PATCH 2/2] vfs: Adds example for semihosting VFS driver --- examples/storage/semihost_vfs/CMakeLists.txt | 6 + examples/storage/semihost_vfs/Makefile | 9 ++ examples/storage/semihost_vfs/README.md | 91 +++++++++++++++ .../storage/semihost_vfs/data/host_file.txt | 104 ++++++++++++++++++ .../storage/semihost_vfs/main/CMakeLists.txt | 4 + .../storage/semihost_vfs/main/component.mk | 4 + .../main/semihost_vfs_example_main.c | 92 ++++++++++++++++ .../storage/semihost_vfs/sdkconfig.defaults | 2 + 8 files changed, 312 insertions(+) create mode 100644 examples/storage/semihost_vfs/CMakeLists.txt create mode 100644 examples/storage/semihost_vfs/Makefile create mode 100644 examples/storage/semihost_vfs/README.md create mode 100644 examples/storage/semihost_vfs/data/host_file.txt create mode 100644 examples/storage/semihost_vfs/main/CMakeLists.txt create mode 100644 examples/storage/semihost_vfs/main/component.mk create mode 100644 examples/storage/semihost_vfs/main/semihost_vfs_example_main.c create mode 100644 examples/storage/semihost_vfs/sdkconfig.defaults diff --git a/examples/storage/semihost_vfs/CMakeLists.txt b/examples/storage/semihost_vfs/CMakeLists.txt new file mode 100644 index 000000000..5f6944151 --- /dev/null +++ b/examples/storage/semihost_vfs/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(semihost_vfs) diff --git a/examples/storage/semihost_vfs/Makefile b/examples/storage/semihost_vfs/Makefile new file mode 100644 index 000000000..6a9282104 --- /dev/null +++ b/examples/storage/semihost_vfs/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 := semihost_vfs + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/storage/semihost_vfs/README.md b/examples/storage/semihost_vfs/README.md new file mode 100644 index 000000000..4728f0866 --- /dev/null +++ b/examples/storage/semihost_vfs/README.md @@ -0,0 +1,91 @@ +# Semihosting VFS driver example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example demonstrates how to use semihosting VFS driver with ESP32. Example does the following steps: + +1. Uses `esp_vfs_semihost_register` function to register exposed host directory in VFS, enabling C standard library and POSIX functions to be used. +2. Redirects `stdout` from the UART to the file on the host using `freopen`. +3. Prints several messages to the redirected. +4. Switches back to UART `stdout` using `freopen`. +5. Opens text file on the host. +6. Reads the file and prints its content on stdout. + +## How to use example + +### Hardware and tools required + +This example does not require any special hardware, and can be run on any common development board. +This example requires [OpenOCD](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#run-openocd). +NOTE: In order to run this example you need OpenOCD version `v0.10.0-esp32-20190313` or later. + +Run OpenOCD using command: +``` +bin/openocd -s share/openocd/scripts -f interface/ftdi/esp32_devkitj_v1.cfg -c 'set ESP_SEMIHOST_BASEDIR '$IDF_PATH/examples/storage/semihost_vfs/data -f board/esp-wroom-32.cfg +``` +This command also configures OpenOCD to expose example project `data` subdirectory to the target's semihosting VFS driver. + +### Configure the project + +If using Make based build system, run `make menuconfig` and set serial port under Serial Flasher Options. + +If using CMake based build system, no configuration is required. + +### Build and flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +make -j4 flash monitor +``` + +Or, for CMake based build system (replace PORT with serial port name): + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example output + +There are two types of outputs produced by example: +1. File `esp32_stdout.txt` in the host directory mounted to the target: + +``` +W (274) example: Switched to semihosted stdout +Semihosted stdout write 0 +Semihosted stdout write 1 +Semihosted stdout write 2 +... +Semihosted stdout write 98 +Semihosted stdout write 99 +W (274) example: Switch to UART stdout +``` + +2. On the boards console: + +``` +W (274) example: Switch to semihosted stdout +W (274) example: Switched back to UART stdout +I (274) example: Wrote 2798 bytes +====================== HOST DATA START ========================= +The following are the graphical (non-control) characters defined by +ISO 8859-1 (1987). Descriptions in words aren't all that helpful, +but they're the best we can do in text. A graphics file illustrating +the character set should be available from the same archive as this +file. + +Hex Description Hex Description + +20 SPACE +... +7D RIGHT CURLY BRACKET FD SMALL LETTER Y WITH ACUTE +7E TILDE FE SMALL LETTER THORN (Icelandic) + FF SMALL LETTER Y WITH DIAERESIS +====================== HOST DATA END ========================= +I (694) example: Read 6121 bytes +``` + diff --git a/examples/storage/semihost_vfs/data/host_file.txt b/examples/storage/semihost_vfs/data/host_file.txt new file mode 100644 index 000000000..f14a8d5b8 --- /dev/null +++ b/examples/storage/semihost_vfs/data/host_file.txt @@ -0,0 +1,104 @@ +The following are the graphical (non-control) characters defined by +ISO 8859-1 (1987). Descriptions in words aren't all that helpful, +but they're the best we can do in text. A graphics file illustrating +the character set should be available from the same archive as this +file. + +Hex Description Hex Description + +20 SPACE +21 EXCLAMATION MARK A1 INVERTED EXCLAMATION MARK +22 QUOTATION MARK A2 CENT SIGN +23 NUMBER SIGN A3 POUND SIGN +24 DOLLAR SIGN A4 CURRENCY SIGN +25 PERCENT SIGN A5 YEN SIGN +26 AMPERSAND A6 BROKEN BAR +27 APOSTROPHE A7 SECTION SIGN +28 LEFT PARENTHESIS A8 DIAERESIS +29 RIGHT PARENTHESIS A9 COPYRIGHT SIGN +2A ASTERISK AA FEMININE ORDINAL INDICATOR +2B PLUS SIGN AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +2C COMMA AC NOT SIGN +2D HYPHEN-MINUS AD SOFT HYPHEN +2E FULL STOP AE REGISTERED SIGN +2F SOLIDUS AF OVERLINE +30 DIGIT ZERO B0 DEGREE SIGN +31 DIGIT ONE B1 PLUS-MINUS SIGN +32 DIGIT TWO B2 SUPERSCRIPT TWO +33 DIGIT THREE B3 SUPERSCRIPT THREE +34 DIGIT FOUR B4 ACUTE ACCENT +35 DIGIT FIVE B5 MICRO SIGN +36 DIGIT SIX B6 PILCROW SIGN +37 DIGIT SEVEN B7 MIDDLE DOT +38 DIGIT EIGHT B8 CEDILLA +39 DIGIT NINE B9 SUPERSCRIPT ONE +3A COLON BA MASCULINE ORDINAL INDICATOR +3B SEMICOLON BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +3C LESS-THAN SIGN BC VULGAR FRACTION ONE QUARTER +3D EQUALS SIGN BD VULGAR FRACTION ONE HALF +3E GREATER-THAN SIGN BE VULGAR FRACTION THREE QUARTERS +3F QUESTION MARK BF INVERTED QUESTION MARK +40 COMMERCIAL AT C0 CAPITAL LETTER A WITH GRAVE +41 CAPITAL LETTER A C1 CAPITAL LETTER A WITH ACUTE +42 CAPITAL LETTER B C2 CAPITAL LETTER A WITH CIRCUMFLEX +43 CAPITAL LETTER C C3 CAPITAL LETTER A WITH TILDE +44 CAPITAL LETTER D C4 CAPITAL LETTER A WITH DIAERESIS +45 CAPITAL LETTER E C5 CAPITAL LETTER A WITH RING ABOVE +46 CAPITAL LETTER F C6 CAPITAL LETTER AE +47 CAPITAL LETTER G C7 CAPITAL LETTER C WITH CEDILLA +48 CAPITAL LETTER H C8 CAPITAL LETTER E WITH GRAVE +49 CAPITAL LETTER I C9 CAPITAL LETTER E WITH ACUTE +4A CAPITAL LETTER J CA CAPITAL LETTER E WITH CIRCUMFLEX +4B CAPITAL LETTER K CB CAPITAL LETTER E WITH DIAERESIS +4C CAPITAL LETTER L CC CAPITAL LETTER I WITH GRAVE +4D CAPITAL LETTER M CD CAPITAL LETTER I WITH ACUTE +4E CAPITAL LETTER N CE CAPITAL LETTER I WITH CIRCUMFLEX +4F CAPITAL LETTER O CF CAPITAL LETTER I WITH DIAERESIS +50 CAPITAL LETTER P D0 CAPITAL LETTER ETH (Icelandic) +51 CAPITAL LETTER Q D1 CAPITAL LETTER N WITH TILDE +52 CAPITAL LETTER R D2 CAPITAL LETTER O WITH GRAVE +53 CAPITAL LETTER S D3 CAPITAL LETTER O WITH ACUTE +54 CAPITAL LETTER T D4 CAPITAL LETTER O WITH CIRCUMFLEX +55 CAPITAL LETTER U D5 CAPITAL LETTER O WITH TILDE +56 CAPITAL LETTER V D6 CAPITAL LETTER O WITH DIAERESIS +57 CAPITAL LETTER W D7 MULTIPLICATION SIGN +58 CAPITAL LETTER X D8 CAPITAL LETTER O WITH STROKE +59 CAPITAL LETTER Y D9 CAPITAL LETTER U WITH GRAVE +5A CAPITAL LETTER Z DA CAPITAL LETTER U WITH ACUTE +5B LEFT SQUARE BRACKET DB CAPITAL LETTER U WITH CIRCUMFLEX +5C REVERSE SOLIDUS DC CAPITAL LETTER U WITH DIAERESIS +5D RIGHT SQUARE BRACKET DD CAPITAL LETTER Y WITH ACUTE +5E CIRCUMFLEX ACCENT DE CAPITAL LETTER THORN (Icelandic) +5F LOW LINE DF SMALL LETTER SHARP S (German) +60 GRAVE ACCENT E0 SMALL LETTER A WITH GRAVE +61 SMALL LETTER A E1 SMALL LETTER A WITH ACUTE +62 SMALL LETTER B E2 SMALL LETTER A WITH CIRCUMFLEX +63 SMALL LETTER C E3 SMALL LETTER A WITH TILDE +64 SMALL LETTER D E4 SMALL LETTER A WITH DIAERESIS +65 SMALL LETTER E E5 SMALL LETTER A WITH RING ABOVE +66 SMALL LETTER F E6 SMALL LETTER AE +67 SMALL LETTER G E7 SMALL LETTER C WITH CEDILLA +68 SMALL LETTER H E8 SMALL LETTER E WITH GRAVE +69 SMALL LETTER I E9 SMALL LETTER E WITH ACUTE +6A SMALL LETTER J EA SMALL LETTER E WITH CIRCUMFLEX +6B SMALL LETTER K EB SMALL LETTER E WITH DIAERESIS +6C SMALL LETTER L EC SMALL LETTER I WITH GRAVE +6D SMALL LETTER M ED SMALL LETTER I WITH ACUTE +6E SMALL LETTER N EE SMALL LETTER I WITH CIRCUMFLEX +6F SMALL LETTER O EF SMALL LETTER I WITH DIAERESIS +70 SMALL LETTER P F0 SMALL LETTER ETH (Icelandic) +71 SMALL LETTER Q F1 SMALL LETTER N WITH TILDE +72 SMALL LETTER R F2 SMALL LETTER O WITH GRAVE +73 SMALL LETTER S F3 SMALL LETTER O WITH ACUTE +74 SMALL LETTER T F4 SMALL LETTER O WITH CIRCUMFLEX +75 SMALL LETTER U F5 SMALL LETTER O WITH TILDE +76 SMALL LETTER V F6 SMALL LETTER O WITH DIAERESIS +77 SMALL LETTER W F7 DIVISION SIGN +78 SMALL LETTER X F8 SMALL LETTER O WITH STROKE +79 SMALL LETTER Y F9 SMALL LETTER U WITH GRAVE +7A SMALL LETTER Z FA SMALL LETTER U WITH ACUTE +7B LEFT CURLY BRACKET FB SMALL LETTER U WITH CIRCUMFLEX +7C VERTICAL LINE FC SMALL LETTER U WITH DIAERESIS +7D RIGHT CURLY BRACKET FD SMALL LETTER Y WITH ACUTE +7E TILDE FE SMALL LETTER THORN (Icelandic) + FF SMALL LETTER Y WITH DIAERESIS diff --git a/examples/storage/semihost_vfs/main/CMakeLists.txt b/examples/storage/semihost_vfs/main/CMakeLists.txt new file mode 100644 index 000000000..34d60ad45 --- /dev/null +++ b/examples/storage/semihost_vfs/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "semihost_vfs_example_main.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/storage/semihost_vfs/main/component.mk b/examples/storage/semihost_vfs/main/component.mk new file mode 100644 index 000000000..a98f634ea --- /dev/null +++ b/examples/storage/semihost_vfs/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/storage/semihost_vfs/main/semihost_vfs_example_main.c b/examples/storage/semihost_vfs/main/semihost_vfs_example_main.c new file mode 100644 index 000000000..edbeabbbe --- /dev/null +++ b/examples/storage/semihost_vfs/main/semihost_vfs_example_main.c @@ -0,0 +1,92 @@ +/* SPIFFS 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 +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_vfs_semihost.h" + +static const char *TAG = "example"; + +#define STRINGIFY(s) STRINGIFY2(s) +#define STRINGIFY2(s) #s + +static uint8_t s_buf[512]; + +void app_main(void) +{ + // Register host FS at '/host'. On the host file will be written/read in the current semihosting dir of OpenOCD + esp_err_t ret = esp_vfs_semihost_register("/host", NULL); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to register semihost driver (%s)!", esp_err_to_name(ret)); + return; + } + + ESP_LOGW(TAG, "Switch to semihosted stdout"); + FILE *fout = freopen("/host/esp32_stdout.txt", "w", stdout); + if (fout == NULL) { + ESP_LOGE(TAG, "Failed to reopen stdout (%d)!", errno); + return; + } + // Increase file buffer to perform data transfers using larger chunks. + // Every read/write triggers breakpoint, so transfering of small chunks is quite inefficient. + setvbuf(fout, (char *)s_buf, _IOFBF, sizeof(s_buf)); + + // this will be printed to the file on host + ESP_LOGW(TAG, "Switched to semihosted stdout"); + for (int i = 0; i < 100; i++) { + // printf is also redirected and sends data to the file on host + printf("Semihosted stdout write %d\n", i); + } + ESP_LOGW(TAG, "Switch to UART stdout"); + fflush(fout); // ensure that all data are sent to the host file + // ftell can also be used, get file size before closing it in `freopen` + int count = ftell(fout); + stdout = freopen("/dev/uart/" STRINGIFY(CONFIG_CONSOLE_UART_NUM), "w", fout); + if (stdout == NULL) { + ESP_LOGE(TAG, "Failed to reopen semihosted stdout (%d)!", errno); + return; + } + // all remaining messages will be printed to UART + ESP_LOGW(TAG, "Switched back to UART stdout"); + ESP_LOGI(TAG, "Wrote %d bytes", count); + + printf("====================== HOST DATA START =========================\n"); + // open() can also be used to access files on the host + int fd = open("/host/host_file.txt", O_RDONLY, 0); + if (fd == -1) { + ESP_LOGE(TAG, "Failed to open file (%d)!", errno); + return; + } + ssize_t read_bytes; + count = 0; + do { + read_bytes = read(fd, s_buf, sizeof(s_buf)); + if(read_bytes == -1) { + ESP_LOGE(TAG, "Failed to read file (%d)!", errno); + } else if(read_bytes > 0) { + fwrite(s_buf, 1, read_bytes, stdout); + count += read_bytes; + } + } while(read_bytes > 0); + printf("====================== HOST DATA END =========================\n"); + ESP_LOGI(TAG, "Read %d bytes", count); + if (close(fd) == -1) { + ESP_LOGE(TAG, "Failed to close file (%d)!", errno); + } + + ret = esp_vfs_semihost_unregister("/host"); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to unregister semihost driver (%s)!", esp_err_to_name(ret)); + } +} diff --git a/examples/storage/semihost_vfs/sdkconfig.defaults b/examples/storage/semihost_vfs/sdkconfig.defaults new file mode 100644 index 000000000..989d01c23 --- /dev/null +++ b/examples/storage/semihost_vfs/sdkconfig.defaults @@ -0,0 +1,2 @@ +# need this to detect that OpenOCD is connected +CONFIG_ESP32_DEBUG_OCDAWARE=y