// Copyright 2017 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. // // Hot It Works // ************ // // This module implements host file I/O protocol on top of apptrace module. // The protocol is enough simple. It sends command with arguments to the host and receives response from it. // Responses contains return values of respective file I/O API. This value is returned to the caller. // Commands has the following format: // * Header. See esp_apptrace_fcmd_hdr_t. // * Operation arguments. See file operation helper structures below. #include #include "esp_app_trace.h" #if CONFIG_APPTRACE_ENABLE #define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL #include "esp_log.h" const static char *TAG = "esp_host_file_io"; #define ESP_APPTRACE_FILE_CMD_FOPEN 0x0 #define ESP_APPTRACE_FILE_CMD_FCLOSE 0x1 #define ESP_APPTRACE_FILE_CMD_FWRITE 0x2 #define ESP_APPTRACE_FILE_CMD_FREAD 0x3 #define ESP_APPTRACE_FILE_CMD_FSEEK 0x4 #define ESP_APPTRACE_FILE_CMD_FTELL 0x5 #define ESP_APPTRACE_FILE_CMD_STOP 0x6 // indicates that there is no files to transfer /** File operation header */ typedef struct { uint8_t cmd; ///< Command ID } esp_apptrace_fcmd_hdr_t; /** Helper structure for fopen */ typedef struct { const char *path; uint16_t path_len; const char *mode; uint16_t mode_len; } esp_apptrace_fopen_args_t; /** Helper structure for fclose */ typedef struct { void *file; } esp_apptrace_fclose_args_t; /** Helper structure for fwrite */ typedef struct { void * buf; size_t size; void * file; } esp_apptrace_fwrite_args_t; /** Helper structure for fread */ typedef struct { size_t size; void * file; } esp_apptrace_fread_args_t; /** Helper structure for fseek */ typedef struct { long offset; int whence; void * file; } esp_apptrace_fseek_args_t; /** Helper structure for ftell */ typedef struct { void *file; } esp_apptrace_ftell_args_t; 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) { esp_err_t ret; esp_apptrace_fcmd_hdr_t *hdr; ESP_EARLY_LOGV(TAG, "%s %d", __func__, cmd); uint8_t *ptr = esp_apptrace_buffer_get(dest, sizeof(*hdr) + args_len, ESP_APPTRACE_TMO_INFINITE); //TODO: finite tmo if (ptr == NULL) { return ESP_ERR_NO_MEM; } hdr = (esp_apptrace_fcmd_hdr_t *)ptr; hdr->cmd = cmd; if (prep_args) { prep_args(ptr + sizeof(hdr->cmd), args); } // now indicate that this buffer is ready to be sent off to host ret = esp_apptrace_buffer_put(dest, ptr, ESP_APPTRACE_TMO_INFINITE);//TODO: finite tmo if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to put apptrace buffer (%d)!", ret); return ret; } ret = esp_apptrace_flush(dest, ESP_APPTRACE_TMO_INFINITE);//TODO: finite tmo if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to flush apptrace buffer (%d)!", ret); return ret; } return ESP_OK; } static esp_err_t esp_apptrace_file_rsp_recv(esp_apptrace_dest_t dest, uint8_t *buf, uint32_t buf_len) { uint32_t tot_rd = 0; while (tot_rd < buf_len) { uint32_t rd_size = buf_len - tot_rd; esp_err_t ret = esp_apptrace_read(dest, buf + tot_rd, &rd_size, ESP_APPTRACE_TMO_INFINITE); //TODO: finite tmo if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to read (%d)!", ret); return ret; } ESP_EARLY_LOGV(TAG, "%s read %d bytes", __FUNCTION__, rd_size); tot_rd += rd_size; } return ESP_OK; } static void esp_apptrace_fopen_args_prepare(uint8_t *buf, void *priv) { esp_apptrace_fopen_args_t *args = priv; memcpy(buf, args->path, args->path_len); memcpy(buf + args->path_len, args->mode, args->mode_len); } void *esp_apptrace_fopen(esp_apptrace_dest_t dest, const char *path, const char *mode) { esp_apptrace_fopen_args_t cmd_args; ESP_EARLY_LOGV(TAG, "esp_apptrace_fopen '%s' '%s'", path, mode); if (path == NULL || mode == NULL) { return 0; } cmd_args.path = path; cmd_args.path_len = strlen(path) + 1; cmd_args.mode = mode; cmd_args.mode_len = strlen(mode) + 1; esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FOPEN, esp_apptrace_fopen_args_prepare, &cmd_args, cmd_args.path_len+cmd_args.mode_len); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret); return NULL; } // now read the answer void *resp; ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret); return NULL; } return resp; } static void esp_apptrace_fclose_args_prepare(uint8_t *buf, void *priv) { esp_apptrace_fclose_args_t *args = priv; memcpy(buf, &args->file, sizeof(args->file)); } int esp_apptrace_fclose(esp_apptrace_dest_t dest, void *stream) { esp_apptrace_fclose_args_t cmd_args; cmd_args.file = stream; esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FCLOSE, esp_apptrace_fclose_args_prepare, &cmd_args, sizeof(cmd_args)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret); return EOF; } // now read the answer int resp; ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret); return EOF; } return resp; } static void esp_apptrace_fwrite_args_prepare(uint8_t *buf, void *priv) { esp_apptrace_fwrite_args_t *args = priv; memcpy(buf, &args->file, sizeof(args->file)); memcpy(buf + sizeof(args->file), args->buf, args->size); } size_t esp_apptrace_fwrite(esp_apptrace_dest_t dest, const void *ptr, size_t size, size_t nmemb, void *stream) { esp_apptrace_fwrite_args_t cmd_args; ESP_EARLY_LOGV(TAG, "esp_apptrace_fwrite f %p l %d", stream, size*nmemb); if (ptr == NULL) { return 0; } cmd_args.buf = (void *)ptr; cmd_args.size = size * nmemb; cmd_args.file = stream; esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FWRITE, esp_apptrace_fwrite_args_prepare, &cmd_args, sizeof(cmd_args.file)+cmd_args.size); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret); return 0; } // now read the answer size_t resp; ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret); return 0; } return resp; } static void esp_apptrace_fread_args_prepare(uint8_t *buf, void *priv) { esp_apptrace_fread_args_t *args = priv; memcpy(buf, &args->file, sizeof(args->file)); memcpy(buf + sizeof(args->file), &args->size, sizeof(args->size)); } size_t esp_apptrace_fread(esp_apptrace_dest_t dest, void *ptr, size_t size, size_t nmemb, void *stream) { esp_apptrace_fread_args_t cmd_args; ESP_EARLY_LOGV(TAG, "esp_apptrace_fread f %p l %d", stream, size*nmemb); if (ptr == NULL) { return 0; } cmd_args.size = size * nmemb; cmd_args.file = stream; esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FREAD, esp_apptrace_fread_args_prepare, &cmd_args, sizeof(cmd_args)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret); return 0; } // now read the answer size_t resp; ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret); return 0; } if (resp > 0) { ret = esp_apptrace_file_rsp_recv(dest, ptr, resp); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to read file data (%d)!", ret); return 0; } } return resp; } static void esp_apptrace_fseek_args_prepare(uint8_t *buf, void *priv) { esp_apptrace_fseek_args_t *args = priv; memcpy(buf, &args->file, sizeof(args->file)); memcpy(buf + sizeof(args->file), &args->offset, sizeof(args->offset)); memcpy(buf + sizeof(args->file) + sizeof(args->offset), &args->whence, sizeof(args->whence)); } int esp_apptrace_fseek(esp_apptrace_dest_t dest, void *stream, long offset, int whence) { esp_apptrace_fseek_args_t cmd_args; ESP_EARLY_LOGV(TAG, "esp_apptrace_fseek f %p o 0x%lx w %d", stream, offset, whence); cmd_args.file = stream; cmd_args.offset = offset; cmd_args.whence = whence; esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FSEEK, esp_apptrace_fseek_args_prepare, &cmd_args, sizeof(cmd_args)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret); return -1; } // now read the answer int resp; ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret); return -1; } return resp; } static void esp_apptrace_ftell_args_prepare(uint8_t *buf, void *priv) { esp_apptrace_ftell_args_t *args = priv; memcpy(buf, &args->file, sizeof(args->file)); } int esp_apptrace_ftell(esp_apptrace_dest_t dest, void *stream) { esp_apptrace_ftell_args_t cmd_args; cmd_args.file = stream; esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FTELL, esp_apptrace_ftell_args_prepare, &cmd_args, sizeof(cmd_args)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret); return -1; } // now read the answer int resp; ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp)); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret); return -1; } return resp; } int esp_apptrace_fstop(esp_apptrace_dest_t dest) { ESP_EARLY_LOGV(TAG, "%s", __func__); esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_STOP, NULL, NULL, 0); if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "Failed to send files transfer stop cmd (%d)!", ret); } return ret; } #endif