1 // Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15
16 #include <stdio.h>
17 #include <stdio_ext.h>
18 #include "esp_log.h"
19 #include "cdc.h"
20 #include "tusb_console.h"
21 #include "tinyusb.h"
22 #include "vfs_tinyusb.h"
23
24 #define STRINGIFY(s) STRINGIFY2(s)
25 #define STRINGIFY2(s) #s
26
27 static const char *TAG = "tusb_console";
28
29 typedef struct {
30 FILE *in;
31 FILE *out;
32 FILE *err;
33 } console_handle_t;
34
35 static console_handle_t con;
36
37
38 /**
39 * @brief Reopen standard streams using a new path
40 *
41 * @param f_in - pointer to a pointer holding a file for in or NULL to don't change stdin
42 * @param f_out - pointer to a pointer holding a file for out or NULL to don't change stdout
43 * @param f_err - pointer to a pointer holding a file for err or NULL to don't change stderr
44 * @param path - mount point
45 * @return esp_err_t ESP_FAIL or ESP_OK
46 */
redirect_std_streams_to(FILE ** f_in,FILE ** f_out,FILE ** f_err,const char * path)47 static esp_err_t redirect_std_streams_to(FILE **f_in, FILE **f_out, FILE **f_err, const char *path)
48 {
49 if (f_in) {
50 *f_in = freopen(path, "r", stdin);
51 if (*f_in == NULL) {
52 ESP_LOGE(TAG, "Failed to reopen in!");
53 return ESP_FAIL;
54 }
55 }
56 if (f_out) {
57 *f_out = freopen(path, "w", stdout);
58 if (*f_out == NULL) {
59 ESP_LOGE(TAG, "Failed to reopen out!");
60 return ESP_FAIL;
61 }
62 }
63 if (f_err) {
64 *f_err = freopen(path, "w", stderr);
65 if (*f_err == NULL) {
66 ESP_LOGE(TAG, "Failed to reopen err!");
67 return ESP_FAIL;
68 }
69 }
70
71 return ESP_OK;
72 }
73
74 /**
75 * @brief Restore output to default
76 *
77 * @param f_in - pointer to a pointer of an in file updated with `redirect_std_streams_to` or NULL to don't change stdin
78 * @param f_out - pointer to a pointer of an out file updated with `redirect_std_streams_to` or NULL to don't change stdout
79 * @param f_err - pointer to a pointer of an err file updated with `redirect_std_streams_to` or NULL to don't change stderr
80 * @return esp_err_t ESP_FAIL or ESP_OK
81 */
restore_std_streams(FILE ** f_in,FILE ** f_out,FILE ** f_err)82 static esp_err_t restore_std_streams(FILE **f_in, FILE **f_out, FILE **f_err)
83 {
84 const char *default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM);
85 if (f_in) {
86 stdin = freopen(default_uart_dev, "r", *f_in);
87 if (stdin == NULL) {
88 ESP_LOGE(TAG, "Failed to reopen stdin!");
89 return ESP_FAIL;
90 }
91 }
92 if (f_out) {
93 stdout = freopen(default_uart_dev, "w", *f_out);
94 if (stdout == NULL) {
95 ESP_LOGE(TAG, "Failed to reopen stdout!");
96 return ESP_FAIL;
97 }
98 }
99 if (f_err) {
100 stderr = freopen(default_uart_dev, "w", *f_err);
101 if (stderr == NULL) {
102 ESP_LOGE(TAG, "Failed to reopen stderr!");
103 return ESP_FAIL;
104 }
105 }
106 return ESP_OK;
107 }
108
esp_tusb_init_console(int cdc_intf)109 esp_err_t esp_tusb_init_console(int cdc_intf)
110 {
111 if (!tinyusb_cdc_initialized(cdc_intf)) {
112 ESP_LOGE(TAG, "Can't init the console because TinyUSB's CDC is not initialized!");
113 return ESP_ERR_INVALID_STATE;
114 }
115 /* Registering TUSB at VFS */
116 int res = esp_vfs_tusb_cdc_register(cdc_intf, NULL);
117 if (res != ESP_OK) {
118 return res;
119 }
120
121 res = redirect_std_streams_to(&con.in, &con.out, &con.err, "/dev/tusb_cdc");
122 if (res != ESP_OK) {
123 return res;
124 }
125
126 return ESP_OK;
127 }
128
esp_tusb_deinit_console(int cdc_intf)129 esp_err_t esp_tusb_deinit_console(int cdc_intf)
130 {
131 if (!tinyusb_cdc_initialized(cdc_intf)) {
132 ESP_LOGE(TAG, "Can't deinit the console because TinyUSB's CDC is not initialized!");
133 return ESP_ERR_INVALID_STATE;
134 }
135
136 int res = restore_std_streams(&con.in, &con.out, &con.err);
137 if (res != ESP_OK) {
138 return res;
139 }
140 esp_vfs_tusb_cdc_unregister(NULL);
141 return ESP_OK;
142 }
143