1 // Copyright 2017 Espressif Systems (Shanghai) PTE 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 // Hot It Works
16 // ************
17 //
18 // This module implements host file I/O protocol on top of apptrace module.
19 // The protocol is enough simple. It sends command with arguments to the host and receives response from it.
20 // Responses contains return values of respective file I/O API. This value is returned to the caller.
21 // Commands has the following format:
22 //   * Header. See esp_apptrace_fcmd_hdr_t.
23 //   * Operation arguments. See file operation helper structures below.
24 
25 #include <string.h>
26 #include "esp_app_trace.h"
27 
28 #if CONFIG_APPTRACE_ENABLE
29 
30 #define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL
31 #include "esp_log.h"
32 const static char *TAG = "esp_host_file_io";
33 
34 #define ESP_APPTRACE_FILE_CMD_FOPEN     0x0
35 #define ESP_APPTRACE_FILE_CMD_FCLOSE    0x1
36 #define ESP_APPTRACE_FILE_CMD_FWRITE    0x2
37 #define ESP_APPTRACE_FILE_CMD_FREAD     0x3
38 #define ESP_APPTRACE_FILE_CMD_FSEEK     0x4
39 #define ESP_APPTRACE_FILE_CMD_FTELL     0x5
40 #define ESP_APPTRACE_FILE_CMD_STOP      0x6 // indicates that there is no files to transfer
41 
42 /** File operation header */
43 typedef struct {
44     uint8_t   cmd; ///< Command ID
45 } esp_apptrace_fcmd_hdr_t;
46 
47 /** Helper structure for fopen */
48 typedef struct {
49     const char *path;
50     uint16_t path_len;
51     const char *mode;
52     uint16_t mode_len;
53 } esp_apptrace_fopen_args_t;
54 
55 /** Helper structure for fclose */
56 typedef struct {
57     void *file;
58 } esp_apptrace_fclose_args_t;
59 
60 /** Helper structure for fwrite */
61 typedef struct {
62     void *  buf;
63     size_t  size;
64     void *  file;
65 } esp_apptrace_fwrite_args_t;
66 
67 /** Helper structure for fread */
68 typedef struct {
69     size_t  size;
70     void *  file;
71 } esp_apptrace_fread_args_t;
72 
73 /** Helper structure for fseek */
74 typedef struct {
75     long    offset;
76     int     whence;
77     void *  file;
78 } esp_apptrace_fseek_args_t;
79 
80 /** Helper structure for ftell */
81 typedef struct {
82     void *file;
83 } esp_apptrace_ftell_args_t;
84 
esp_apptrace_file_cmd_send(esp_apptrace_dest_t dest,uint8_t cmd,void (* prep_args)(uint8_t *,void *),void * args,uint32_t args_len)85 static esp_err_t esp_apptrace_file_cmd_send(esp_apptrace_dest_t dest, uint8_t cmd, void (*prep_args)(uint8_t *, void *), void *args, uint32_t args_len)
86 {
87     esp_err_t ret;
88     esp_apptrace_fcmd_hdr_t *hdr;
89 
90     ESP_EARLY_LOGV(TAG, "%s %d", __func__, cmd);
91     uint8_t *ptr = esp_apptrace_buffer_get(dest, sizeof(*hdr) + args_len, ESP_APPTRACE_TMO_INFINITE); //TODO: finite tmo
92     if (ptr == NULL) {
93         return ESP_ERR_NO_MEM;
94     }
95 
96     hdr = (esp_apptrace_fcmd_hdr_t *)ptr;
97     hdr->cmd = cmd;
98     if (prep_args) {
99         prep_args(ptr + sizeof(hdr->cmd), args);
100     }
101 
102     // now indicate that this buffer is ready to be sent off to host
103     ret = esp_apptrace_buffer_put(dest, ptr, ESP_APPTRACE_TMO_INFINITE);//TODO: finite tmo
104     if (ret != ESP_OK) {
105         ESP_EARLY_LOGE(TAG, "Failed to put apptrace buffer (%d)!", ret);
106         return ret;
107     }
108 
109     ret = esp_apptrace_flush(dest, ESP_APPTRACE_TMO_INFINITE);//TODO: finite tmo
110     if (ret != ESP_OK) {
111         ESP_EARLY_LOGE(TAG, "Failed to flush apptrace buffer (%d)!", ret);
112         return ret;
113     }
114 
115     return ESP_OK;
116 }
117 
esp_apptrace_file_rsp_recv(esp_apptrace_dest_t dest,uint8_t * buf,uint32_t buf_len)118 static esp_err_t esp_apptrace_file_rsp_recv(esp_apptrace_dest_t dest, uint8_t *buf, uint32_t buf_len)
119 {
120     uint32_t tot_rd = 0;
121     while (tot_rd < buf_len) {
122         uint32_t rd_size = buf_len - tot_rd;
123         esp_err_t ret = esp_apptrace_read(dest, buf + tot_rd, &rd_size, ESP_APPTRACE_TMO_INFINITE); //TODO: finite tmo
124         if (ret != ESP_OK) {
125             ESP_EARLY_LOGE(TAG, "Failed to read (%d)!", ret);
126             return ret;
127         }
128         ESP_EARLY_LOGV(TAG, "%s read %d bytes", __FUNCTION__, rd_size);
129         tot_rd += rd_size;
130     }
131 
132     return ESP_OK;
133 }
134 
esp_apptrace_fopen_args_prepare(uint8_t * buf,void * priv)135 static void esp_apptrace_fopen_args_prepare(uint8_t *buf, void *priv)
136 {
137     esp_apptrace_fopen_args_t *args = priv;
138 
139     memcpy(buf, args->path, args->path_len);
140     memcpy(buf + args->path_len, args->mode, args->mode_len);
141 }
142 
esp_apptrace_fopen(esp_apptrace_dest_t dest,const char * path,const char * mode)143 void *esp_apptrace_fopen(esp_apptrace_dest_t dest, const char *path, const char *mode)
144 {
145     esp_apptrace_fopen_args_t cmd_args;
146 
147     ESP_EARLY_LOGV(TAG, "esp_apptrace_fopen '%s' '%s'", path, mode);
148     if (path == NULL || mode == NULL) {
149         return 0;
150     }
151 
152     cmd_args.path = path;
153     cmd_args.path_len = strlen(path) + 1;
154     cmd_args.mode = mode;
155     cmd_args.mode_len = strlen(mode) + 1;
156 
157     esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FOPEN, esp_apptrace_fopen_args_prepare,
158                         &cmd_args, cmd_args.path_len+cmd_args.mode_len);
159     if (ret != ESP_OK) {
160         ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
161         return NULL;
162     }
163 
164     // now read the answer
165     void *resp;
166     ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
167     if (ret != ESP_OK) {
168         ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
169         return NULL;
170     }
171 
172     return resp;
173 }
174 
esp_apptrace_fclose_args_prepare(uint8_t * buf,void * priv)175 static void esp_apptrace_fclose_args_prepare(uint8_t *buf, void *priv)
176 {
177     esp_apptrace_fclose_args_t *args = priv;
178 
179     memcpy(buf, &args->file, sizeof(args->file));
180 }
181 
esp_apptrace_fclose(esp_apptrace_dest_t dest,void * stream)182 int esp_apptrace_fclose(esp_apptrace_dest_t dest, void *stream)
183 {
184     esp_apptrace_fclose_args_t cmd_args;
185 
186     cmd_args.file = stream;
187     esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FCLOSE, esp_apptrace_fclose_args_prepare,
188                         &cmd_args, sizeof(cmd_args));
189     if (ret != ESP_OK) {
190         ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
191         return EOF;
192     }
193 
194     // now read the answer
195     int resp;
196     ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
197     if (ret != ESP_OK) {
198         ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
199         return EOF;
200     }
201 
202     return resp;
203 }
204 
esp_apptrace_fwrite_args_prepare(uint8_t * buf,void * priv)205 static void esp_apptrace_fwrite_args_prepare(uint8_t *buf, void *priv)
206 {
207     esp_apptrace_fwrite_args_t *args = priv;
208 
209     memcpy(buf, &args->file, sizeof(args->file));
210     memcpy(buf + sizeof(args->file), args->buf, args->size);
211 }
212 
esp_apptrace_fwrite(esp_apptrace_dest_t dest,const void * ptr,size_t size,size_t nmemb,void * stream)213 size_t esp_apptrace_fwrite(esp_apptrace_dest_t dest, const void *ptr, size_t size, size_t nmemb, void *stream)
214 {
215     esp_apptrace_fwrite_args_t cmd_args;
216 
217     ESP_EARLY_LOGV(TAG, "esp_apptrace_fwrite f %p l %d", stream, size*nmemb);
218 
219     if (ptr == NULL) {
220         return 0;
221     }
222 
223     cmd_args.buf = (void *)ptr;
224     cmd_args.size = size * nmemb;
225     cmd_args.file = stream;
226     esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FWRITE, esp_apptrace_fwrite_args_prepare,
227                         &cmd_args, sizeof(cmd_args.file)+cmd_args.size);
228     if (ret != ESP_OK) {
229         ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
230         return 0;
231     }
232 
233     // now read the answer
234     size_t resp;
235     ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
236     if (ret != ESP_OK) {
237         ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
238         return 0;
239     }
240 
241     return resp;
242 }
243 
esp_apptrace_fread_args_prepare(uint8_t * buf,void * priv)244 static void esp_apptrace_fread_args_prepare(uint8_t *buf, void *priv)
245 {
246     esp_apptrace_fread_args_t *args = priv;
247 
248     memcpy(buf, &args->file, sizeof(args->file));
249     memcpy(buf + sizeof(args->file), &args->size, sizeof(args->size));
250 }
251 
esp_apptrace_fread(esp_apptrace_dest_t dest,void * ptr,size_t size,size_t nmemb,void * stream)252 size_t esp_apptrace_fread(esp_apptrace_dest_t dest, void *ptr, size_t size, size_t nmemb, void *stream)
253 {
254     esp_apptrace_fread_args_t cmd_args;
255 
256     ESP_EARLY_LOGV(TAG, "esp_apptrace_fread f %p l %d", stream, size*nmemb);
257 
258     if (ptr == NULL) {
259         return 0;
260     }
261 
262     cmd_args.size = size * nmemb;
263     cmd_args.file = stream;
264     esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FREAD, esp_apptrace_fread_args_prepare,
265                         &cmd_args, sizeof(cmd_args));
266     if (ret != ESP_OK) {
267         ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
268         return 0;
269     }
270 
271     // now read the answer
272     size_t resp;
273     ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
274     if (ret != ESP_OK) {
275         ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
276         return 0;
277     }
278     if (resp > 0) {
279         ret = esp_apptrace_file_rsp_recv(dest, ptr, resp);
280         if (ret != ESP_OK) {
281             ESP_EARLY_LOGE(TAG, "Failed to read file data (%d)!", ret);
282             return 0;
283         }
284     }
285     return resp;
286 }
287 
esp_apptrace_fseek_args_prepare(uint8_t * buf,void * priv)288 static void esp_apptrace_fseek_args_prepare(uint8_t *buf, void *priv)
289 {
290     esp_apptrace_fseek_args_t *args = priv;
291 
292     memcpy(buf, &args->file, sizeof(args->file));
293     memcpy(buf + sizeof(args->file), &args->offset, sizeof(args->offset));
294     memcpy(buf + sizeof(args->file) + sizeof(args->offset), &args->whence, sizeof(args->whence));
295 }
296 
esp_apptrace_fseek(esp_apptrace_dest_t dest,void * stream,long offset,int whence)297 int esp_apptrace_fseek(esp_apptrace_dest_t dest, void *stream, long offset, int whence)
298 {
299     esp_apptrace_fseek_args_t cmd_args;
300 
301     ESP_EARLY_LOGV(TAG, "esp_apptrace_fseek f %p o 0x%lx w %d", stream, offset, whence);
302 
303     cmd_args.file = stream;
304     cmd_args.offset = offset;
305     cmd_args.whence = whence;
306     esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FSEEK, esp_apptrace_fseek_args_prepare,
307                         &cmd_args, sizeof(cmd_args));
308     if (ret != ESP_OK) {
309         ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
310         return -1;
311     }
312 
313     // now read the answer
314     int resp;
315     ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
316     if (ret != ESP_OK) {
317         ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
318         return -1;
319     }
320 
321     return resp;
322 }
323 
esp_apptrace_ftell_args_prepare(uint8_t * buf,void * priv)324 static void esp_apptrace_ftell_args_prepare(uint8_t *buf, void *priv)
325 {
326     esp_apptrace_ftell_args_t *args = priv;
327 
328     memcpy(buf, &args->file, sizeof(args->file));
329 }
330 
esp_apptrace_ftell(esp_apptrace_dest_t dest,void * stream)331 int esp_apptrace_ftell(esp_apptrace_dest_t dest, void *stream)
332 {
333     esp_apptrace_ftell_args_t cmd_args;
334 
335     cmd_args.file = stream;
336     esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FTELL, esp_apptrace_ftell_args_prepare,
337                         &cmd_args, sizeof(cmd_args));
338     if (ret != ESP_OK) {
339         ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
340         return -1;
341     }
342 
343     // now read the answer
344     int resp;
345     ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
346     if (ret != ESP_OK) {
347         ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
348         return -1;
349     }
350 
351     return resp;
352 }
353 
esp_apptrace_fstop(esp_apptrace_dest_t dest)354 int esp_apptrace_fstop(esp_apptrace_dest_t dest)
355 {
356     ESP_EARLY_LOGV(TAG, "%s", __func__);
357     esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_STOP, NULL, NULL, 0);
358     if (ret != ESP_OK) {
359         ESP_EARLY_LOGE(TAG, "Failed to send files transfer stop cmd (%d)!", ret);
360     }
361     return ret;
362 }
363 
364 #endif
365