Преглед изворни кода

Merge branch 'feature/semihost_vfs' into 'master'

Adds semihosting VFS driver

Closes IDF-367

See merge request idf/esp-idf!4145
Ivan Grokhotkov пре 7 година
родитељ
комит
3a779380a7

+ 2 - 1
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)

+ 16 - 0
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

+ 46 - 0
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

+ 216 - 0
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 <string.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#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;
+}

+ 1 - 0
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 \

+ 6 - 0
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)

+ 9 - 0
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
+

+ 91 - 0
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
+```
+

+ 104 - 0
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

+ 4 - 0
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()

+ 4 - 0
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.)

+ 92 - 0
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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#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));
+    }
+}

+ 2 - 0
examples/storage/semihost_vfs/sdkconfig.defaults

@@ -0,0 +1,2 @@
+# need this to detect that OpenOCD is connected
+CONFIG_ESP32_DEBUG_OCDAWARE=y