From c270cfb5cb5de8e6ff5908146ec04fb521688c3e Mon Sep 17 00:00:00 2001 From: udoudou <949797067@qq.com> Date: Thu, 25 Jan 2024 00:55:37 +0800 Subject: [PATCH 1/6] Add MTP function implementation --- .gitignore | 2 +- .../cherryusb_device_cdc/main/CMakeLists.txt | 6 +- .../cherryusb_device_mtp/CMakeLists.txt | 6 + .../components/esp_mtp/CMakeLists.txt | 4 + .../components/esp_mtp/esp_mtp.c | 1082 +++++++++++++++++ .../components/esp_mtp/esp_mtp_helper.c | 165 +++ .../components/esp_mtp/include/esp_mtp.h | 39 + .../components/esp_mtp/include/esp_mtp_def.h | 426 +++++++ .../esp_mtp/include/esp_mtp_helper.h | 41 + .../components/usb_mtp/CMakeLists.txt | 4 + .../components/usb_mtp/include/usb_mtp.h | 55 + .../components/usb_mtp/usb_mtp.c | 140 +++ .../cherryusb_device_mtp/main/CMakeLists.txt | 3 + .../main/Kconfig.projbuild | 57 + .../main/device_mtp_main.c | 206 ++++ .../main/idf_component.yml | 8 + .../main/sd_card_example_main.c | 97 ++ .../cherryusb_device_mtp/sdkconfig.defaults | 3 + 18 files changed, 2339 insertions(+), 5 deletions(-) create mode 100644 examples/device/cherryusb_device_mtp/CMakeLists.txt create mode 100644 examples/device/cherryusb_device_mtp/components/esp_mtp/CMakeLists.txt create mode 100644 examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c create mode 100644 examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c create mode 100644 examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp.h create mode 100644 examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_def.h create mode 100644 examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_helper.h create mode 100644 examples/device/cherryusb_device_mtp/components/usb_mtp/CMakeLists.txt create mode 100644 examples/device/cherryusb_device_mtp/components/usb_mtp/include/usb_mtp.h create mode 100644 examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c create mode 100644 examples/device/cherryusb_device_mtp/main/CMakeLists.txt create mode 100644 examples/device/cherryusb_device_mtp/main/Kconfig.projbuild create mode 100644 examples/device/cherryusb_device_mtp/main/device_mtp_main.c create mode 100644 examples/device/cherryusb_device_mtp/main/idf_component.yml create mode 100644 examples/device/cherryusb_device_mtp/main/sd_card_example_main.c create mode 100644 examples/device/cherryusb_device_mtp/sdkconfig.defaults diff --git a/.gitignore b/.gitignore index 61eb050..0f5a07c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ managed_components dependencies.lock # VS Code Settings -.vscode/ \ No newline at end of file +.vscode/ diff --git a/examples/device/cherryusb_device_cdc/main/CMakeLists.txt b/examples/device/cherryusb_device_cdc/main/CMakeLists.txt index b7d0e2a..6795aea 100644 --- a/examples/device/cherryusb_device_cdc/main/CMakeLists.txt +++ b/examples/device/cherryusb_device_cdc/main/CMakeLists.txt @@ -1,5 +1,3 @@ -idf_component_register(SRCS - "device_cdc_main.c" - INCLUDE_DIRS - "." +idf_component_register(SRCS "device_cdc_main.c" + INCLUDE_DIRS "." ) diff --git a/examples/device/cherryusb_device_mtp/CMakeLists.txt b/examples/device/cherryusb_device_mtp/CMakeLists.txt new file mode 100644 index 0000000..ed957fe --- /dev/null +++ b/examples/device/cherryusb_device_mtp/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(cherryusb_device_mtp) diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/CMakeLists.txt b/examples/device/cherryusb_device_mtp/components/esp_mtp/CMakeLists.txt new file mode 100644 index 0000000..d0be680 --- /dev/null +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "esp_mtp.c" "esp_mtp_helper.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES +) diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c new file mode 100644 index 0000000..37c219f --- /dev/null +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c @@ -0,0 +1,1082 @@ +/* + * Copyright (c) 2024, udoudou + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_mtp.h" +#include "esp_mtp_def.h" +#include "esp_mtp_helper.h" + +#include "string.h" +#include "dirent.h" +#include "sys/stat.h" +#include "unistd.h" +#include "fcntl.h" + +#include "esp_log.h" + +static char *TAG = "esp_mtp"; + +#define ASYNC_READ_NOTIFY_BIT BIT0 +#define ASYNC_WRITE_NOTIFY_BIT BIT1 + +typedef struct esp_mtp { + void *pipe_context; + int (*read)(void *pipe_context, uint8_t *buffer, int len); + int (*write)(void *pipe_context, const uint8_t *buffer, int len); + esp_mtp_flags_t flags; + TaskHandle_t task_hdl; + int async_read_len; + esp_mtp_file_handle_list_t handle_list; + uint32_t buffer_size; + uint8_t buff[0]; +}esp_mtp_t; + +const mtp_operation_code_t supported_operation_codes[] = { + MTP_OPERATION_GET_DEVICE_INFO, + MTP_OPERATION_OPEN_SESSION, + MTP_OPERATION_CLOSE_SESSION, + MTP_OPERATION_GET_STORAGE_IDS, + MTP_OPERATION_GET_STORAGE_INFO, + MTP_OPERATION_GET_OBJECT_HANDLES, + MTP_OPERATION_GET_OBJECT_INFO, + MTP_OPERATION_GET_OBJECT, + MTP_OPERATION_DELETE_OBJECT, + MTP_OPERATION_SEND_OBJECT_INFO, + MTP_OPERATION_SEND_OBJECT, + MTP_OPERATION_GET_PARTIAL_OBJECT, + + // MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, + // MTP_OPERATION_GET_OBJECT_PROP_DESC, + // MTP_OPERATION_GET_OBJECT_PROP_VALUE, + // MTP_OPERATION_SET_OBJECT_PROP_VALUE +}; + +//todo 不必要 +const mtp_operation_code_t supported_event_codes[] = { + +}; + +static void check_usb_len_mps_and_send_end(esp_mtp_handle_t handle, uint32_t len) +{ + bool need_send_end = false; + if (handle->flags & (ESP_MTP_FLAG_USB_FS | ESP_MTP_FLAG_USB_HS)) { + if (handle->flags & ESP_MTP_FLAG_USB_FS) { + if (len % 64 == 0) { + need_send_end = true; + } + } else { + if (len % 512 == 0) { + need_send_end = true; + } + } + if (need_send_end) { + handle->write(handle->pipe_context, NULL, 0); + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + } + } +} + + +static mtp_response_code_t open_session(esp_mtp_handle_t handle) +{ + return MTP_RESPONSE_OK; +} + +static mtp_response_code_t get_thumb(esp_mtp_handle_t handle) +{ + return MTP_RESPONSE_OK; +} + +static mtp_response_code_t get_device_info(esp_mtp_handle_t handle) +{ + mtp_container_t *container = (mtp_container_t *)handle->buff; + uint8_t *data = container->data; + container->type = MTP_CONTAINER_DATA; + *(uint16_t *)data = MTP_STANDARD_VERSION; // Standard Version + data += 2; + *(uint32_t *)data = MTP_VENDOR_EXTN_ID; // MTP Vendor Extension ID + data += 4; + *(uint16_t *)data = MTP_VENDOR_EXTN_VERSION; // MTP Version + data += 2; + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16(MTP_VENDOR_EXTENSIONDESC_CHAR, data + 1, data); // MTP Extensions + *(mtp_functional_mode_t *)data = MTP_FUNCTIONAL_STANDARD; // Functional Mode + data += sizeof(mtp_functional_mode_t); + + *(uint32_t *)data = sizeof(supported_operation_codes) / sizeof(supported_operation_codes[0]); + data += 4; + memcpy(data, supported_operation_codes, sizeof(supported_operation_codes)); // Operations Supported + data += sizeof(supported_operation_codes); + + *(uint32_t *)data = sizeof(supported_event_codes) / sizeof(supported_event_codes[0]); + data += 4; + memcpy(data, supported_event_codes, sizeof(supported_event_codes)); // Events Supported + data += sizeof(supported_event_codes); + + // Supported device properties + *(uint32_t *)data = 0; + data += 4; + // *(uint32_t *)data = 2; + // data += 4; + // *(uint16_t *)data = MTP_DEV_PROP_BATTERY_LEVEL; + // data += 2; + // *(uint16_t *)data = MTP_DEV_PROP_DEVICE_FRIENDLY_NAME; + // data += 2; + + // Supported formats + *(uint32_t *)data = 0; + data += 4; + + // Playback Formats + // *(uint32_t *)data = 0; + // data += 4; + *(uint32_t *)data = 2; + data += 4; + *(mtp_object_format_code_t *)data = MTP_OBJECT_FORMAT_UNDEFINED; + data += 2; + *(mtp_object_format_code_t *)data = MTP_OBJECT_FORMAT_ASSOCIATION; + data += sizeof(mtp_object_format_code_t); + + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16("Espressif", data + 1, data); // Manufacturer + + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16("ESP32-S3", data + 1, data); // Model + + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16("0.0.1", data + 1, data); // Device Version + + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16("123456", data + 1, data); // Serial Number + + container->len = data - handle->buff; + handle->write(handle->pipe_context, handle->buff, container->len); + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + check_usb_len_mps_and_send_end(handle, container->len); + + return MTP_RESPONSE_OK; +} + +static mtp_response_code_t get_storage_ids(esp_mtp_handle_t handle) +{ + mtp_container_t *container = (mtp_container_t *)handle->buff; + uint8_t *data = container->data; + container->type = MTP_CONTAINER_DATA; + //Storage ID array + *(uint32_t *)data = 1; + data += 4; + *(uint32_t *)data = 0x010001; + data += 4; + container->len = data - handle->buff; + handle->write(handle->pipe_context, handle->buff, container->len); + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + // 长度不会达到 MPS,无需检查 + // check_usb_len_mps_and_send_end(handle, container->len); + + return MTP_RESPONSE_OK; +} + +static mtp_response_code_t get_storage_info(esp_mtp_handle_t handle) +{ + mtp_container_t *container = (mtp_container_t *)handle->buff; + uint8_t *data = container->data; + // container->operation.get_storage_info.storage_id; + + container->type = MTP_CONTAINER_DATA; + //StorageInfo dataset + + *(mtp_storage_type_t *)data = MTP_STORAGE_FIXED_RAM; // Storage Type + data += sizeof(mtp_storage_type_t); + + *(mtp_file_system_type_t *)data = MTP_FILE_SYSTEM_GENERIC_HIERARCH; // Filesystem Type + data += sizeof(mtp_file_system_type_t); + + *(mtp_access_cap_t *)data = MTP_ACCESS_CAP_RW; // Access Capability + data += sizeof(mtp_access_cap_t); + + + *(uint64_t *)data = 1024 * 1024 * 1024; // Max Capacity + data += sizeof(uint64_t); + + *(uint64_t *)data = 1024; // Free space in Bytes + data += sizeof(uint64_t); + + *(uint32_t *)data = 0xFFFFFFFF; // Free Space In Objects + data += sizeof(uint32_t); + + + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16("test", data + 1, data); // Storage Description + + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16("0", data + 1, data); // Volume Identifier + + container->len = data - handle->buff; + handle->write(handle->pipe_context, handle->buff, container->len); + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + check_usb_len_mps_and_send_end(handle, container->len); + return MTP_RESPONSE_OK; +} + +static mtp_response_code_t get_object_handles(esp_mtp_handle_t handle) +{ + mtp_container_t *container = (mtp_container_t *)handle->buff; + uint8_t *data = container->data; + uint32_t parent_handle; + + //todo + // 0xFFFFFFFF 代表获取所有 storage 上的 object_handles + // container->operation.get_object_handles.storage_id + + if (container->operation.get_object_handles.object_format_code != 0) { + return MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED; + } + + parent_handle = container->operation.get_object_handles.parent_handle; + // 0xFFFFFFFF 代表获取根目录下的 object_handles + if (parent_handle == 0xFFFFFFFF) { + parent_handle = 0; + } + + container->type = MTP_CONTAINER_DATA; + *(uint32_t *)container->data = handle->buff + handle->buffer_size - container->data - 4; // Number of Object Handles + //todo 暂未支持多个 storage + data = esp_mtp_file_list_fill_handle_array(&handle->handle_list, parent_handle, container->data + 4, (uint32_t *)container->data); // Object Handles + + if (*(uint32_t *)container->data == 0) { + strcpy((char *)container->data, "/sdcard"); + data = container->data + strlen((char *)container->data); + if (parent_handle != 0) { + if (esp_mtp_file_list_find(&handle->handle_list, parent_handle, (char *)data, handle->buff + handle->buffer_size - data) == NULL) { + return MTP_RESPONSE_INVALID_PARENT_OBJECT; + } + } + + { + DIR *dir; + struct dirent *file; + dir = opendir((char *)container->data); + if (dir == NULL) { + ESP_LOGW(TAG, "Failed to open dir for reading:%s", container->data); + return MTP_RESPONSE_INVALID_PARENT_OBJECT; + } + data = container->data + sizeof(uint32_t); + while ((file = readdir(dir)) != NULL) { + *(uint32_t *)data = esp_mtp_file_list_add(&handle->handle_list, 0x00010001, parent_handle, file->d_name); + if (*(uint32_t *)data == 0) { + ESP_LOGW(TAG, "add file list fail"); + break; + } + data += sizeof(uint32_t); + } + closedir(dir); + *(uint32_t *)container->data = (data - (container->data + sizeof(uint32_t))) / sizeof(uint32_t); + } + } + + container->len = data - handle->buff; + handle->write(handle->pipe_context, handle->buff, container->len); + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + check_usb_len_mps_and_send_end(handle, container->len); + return MTP_RESPONSE_OK; +} + +static mtp_response_code_t get_object_info(esp_mtp_handle_t handle) +{ + uint8_t *data; + uint32_t object_handle; + const esp_mtp_file_entry_t *entry; + mtp_container_t *container = (mtp_container_t *)handle->buff; + + object_handle = container->operation.get_object_info.object_handle; + strcpy((char *)container->data, "/sdcard"); + data = container->data + strlen((char *)container->data); + entry = esp_mtp_file_list_find(&handle->handle_list, object_handle, (char *)data, handle->buff + handle->buffer_size - data); + if (entry == NULL) { + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + ESP_LOGW(TAG, "%s %s", __FUNCTION__, (char *)container->data); + struct stat st; + if (stat((char *)container->data, &st) != 0) { + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + + container->type = MTP_CONTAINER_DATA; + data = container->data; + + *(uint32_t *)data = entry->storage_id; // StorageID + *(uint32_t *)data = 0x010001; + data += sizeof(uint32_t); + + if (S_ISDIR(st.st_mode)) { + *(uint16_t *)data = MTP_OBJECT_FORMAT_ASSOCIATION; // ObjectFormat Code + } else { + *(uint16_t *)data = MTP_OBJECT_FORMAT_UNDEFINED; // ObjectFormat Code + } + data += sizeof(uint16_t); + + *(uint16_t *)data = 0x0000; // Protection Status + data += sizeof(uint16_t); + + *(uint32_t *)data = st.st_size; // Object Compressed Size + data += sizeof(uint32_t); + + *(uint16_t *)data = 0x0000; // Thumb Format(未使用) + data += sizeof(uint16_t); + + *(uint32_t *)data = 0x0000; // Thumb Compressed Size(未使用) + data += sizeof(uint32_t); + + *(uint32_t *)data = 0x0000; // Thumb Pix Width(未使用) + data += sizeof(uint32_t); + + *(uint32_t *)data = 0x0000; // Thumb Pix Height(未使用) + data += sizeof(uint32_t); + + *(uint32_t *)data = 0x0000; // Image Pix Width(未使用) + data += sizeof(uint32_t); + + *(uint32_t *)data = 0x0000; // Image Pix Height(未使用) + data += sizeof(uint32_t); + + *(uint32_t *)data = 0x0000; // Image Bit Depth(未使用) + data += sizeof(uint32_t); + + *(uint32_t *)data = entry->parent; // Parent Object + data += sizeof(uint32_t); + + if (S_ISDIR(st.st_mode)) { + *(mtp_association_type_t *)data = MTP_ASSOCIATION_GENERIC_FOLDER; // Association Type + } else { + *(mtp_association_type_t *)data = MTP_ASSOCIATION_UNDEFINED; // Association Type + } + data += sizeof(mtp_association_type_t); + + *(uint32_t *)data = 0x0; // Association Description + data += sizeof(uint32_t); + + *(uint32_t *)data = 0x0; // Sequence Number(未使用) + data += sizeof(uint32_t); + + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16(entry->name, data + 1, data); // Filename + + struct tm timeinfo; + char timestr[48]; + // Date Created "YYYYMMDDThhmmss.s" + time_t t = st.st_ctime; + localtime_r(&t, &timeinfo); + snprintf(timestr, sizeof(timestr), "%.4d%.2d%.2dT%.2d%.2d%.2d", 1900 + timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16(timestr, data + 1, data); + + // Date Modified "YYYYMMDDThhmmss.s" + t = st.st_mtime; + localtime_r(&t, &timeinfo); + snprintf(timestr, sizeof(timestr), "%.4d%.2d%.2dT%.2d%.2d%.2d", 1900 + timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); + *data = handle->buff + handle->buffer_size - data; + data = esp_mtp_utf8_to_utf16(timestr, data + 1, data); + + // *data = handle->buff + handle->buffer_size - data; + // data = esp_mtp_utf8_to_utf16("", data + 1, data); // Keywords(未使用) + *data = 0x0; + data++; + + container->len = data - handle->buff; + handle->write(handle->pipe_context, handle->buff, container->len); + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + check_usb_len_mps_and_send_end(handle, container->len); + return MTP_RESPONSE_OK; +} + +static mtp_response_code_t _get_object_common(esp_mtp_handle_t handle, uint32_t object_handle, uint32_t offset, uint32_t max_bytes, uint32_t *actual_bytes) +{ + uint8_t *data; + mtp_container_t *container = (mtp_container_t *)handle->buff; + + strcpy((char *)container->data, "/sdcard"); + data = container->data + strlen((char *)container->data); + if (esp_mtp_file_list_find(&handle->handle_list, object_handle, (char *)data, handle->buff + handle->buffer_size - data) == NULL) { + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + ESP_LOGW(TAG, "%s %s", __FUNCTION__, (char *)container->data); + + int fd; + struct stat st; + if (stat((char *)container->data, &st) != 0) { + return MTP_RESPONSE_ACCESS_DENIED; + } + fd = open((char *)container->data, O_RDONLY); + if (fd < 0) { + return MTP_RESPONSE_ACCESS_DENIED; + } + if (offset) { + if (offset > st.st_size) { + offset = st.st_size; + } + lseek(fd, offset, SEEK_SET); + } + + uint32_t trans_id; + trans_id = container->trans_id; + + uint32_t file_size; + uint32_t max_len; + uint32_t read_len; + uint32_t last_write_len = 0; + + file_size = st.st_size - offset; + if (file_size > max_bytes) { + st.st_size = max_bytes; + file_size = max_bytes; + } + + container->type = MTP_CONTAINER_DATA; + container->len = MTP_CONTAINER_HEAD_LEN + st.st_size; + data = container->data; + + max_len = handle->buffer_size - MTP_CONTAINER_HEAD_LEN; + max_len = max_len / 2; + max_len = max_len & (~0x1ff); //512对齐 + ESP_LOGD(TAG, "max_len:%"PRIu32, max_len); + mtp_response_code_t res = MTP_RESPONSE_OK; + + if (file_size == 0) { + handle->write(handle->pipe_context, handle->buff, MTP_CONTAINER_HEAD_LEN); + last_write_len = MTP_CONTAINER_HEAD_LEN; + } + + while (file_size) { + read_len = file_size > max_len ? max_len : file_size; + if (read(fd, data, read_len) != read_len) { + ESP_LOGE(TAG, "file read error"); + last_write_len = 0; + res = MTP_RESPONSE_INCOMPLETE_TRANSFER; + break; + } + if (file_size != st.st_size) { + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + } + file_size -= read_len; + last_write_len = file_size > 0 ? read_len : MTP_CONTAINER_HEAD_LEN + read_len; + + if (data == container->data) { + handle->write(handle->pipe_context, handle->buff, last_write_len); + data = container->data + max_len; + } else { + handle->write(handle->pipe_context, handle->buff + max_len, last_write_len); + memcpy(handle->buff, data + read_len - MTP_CONTAINER_HEAD_LEN, MTP_CONTAINER_HEAD_LEN); + data = container->data; + } + } + close(fd); + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + check_usb_len_mps_and_send_end(handle, last_write_len); + if (actual_bytes) { + *actual_bytes = st.st_size - file_size; + } + container->trans_id = trans_id; + return res; +} + +static mtp_response_code_t get_object(esp_mtp_handle_t handle) +{ +#if 0 + uint8_t *data; + uint32_t object_handle; + mtp_container_t *container = (mtp_container_t *)handle->buff; + object_handle = container->operation.get_object.object_handle; + strcpy((char *)container->data, "/sdcard"); + data = container->data + strlen((char *)container->data); + if (esp_mtp_file_list_find(&handle->handle_list, object_handle, (char *)data, handle->buff + handle->buffer_size - data) == NULL) { + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + ESP_LOGW(TAG, "%s %s", __FUNCTION__, (char *)container->data); + + int fd; + struct stat st; + if (stat((char *)container->data, &st) != 0) { + return MTP_RESPONSE_ACCESS_DENIED; + } + fd = open((char *)container->data, O_RDONLY); + if (fd < 0) { + return MTP_RESPONSE_ACCESS_DENIED; + } + + uint32_t trans_id; + trans_id = container->trans_id; + + uint32_t file_size; + uint32_t max_len; + uint32_t read_len; + uint32_t last_write_len = 0; + file_size = st.st_size; + container->type = MTP_CONTAINER_DATA; + container->len = MTP_CONTAINER_HEAD_LEN + st.st_size; + data = container->data; + + max_len = handle->buffer_size - MTP_CONTAINER_HEAD_LEN; + max_len = max_len / 2; + max_len = max_len & (~0x1ff); //512对齐 + ESP_LOGD(TAG, "max_len:%"PRIu32, max_len); + mtp_response_code_t res = MTP_RESPONSE_OK; + + while (file_size) { + read_len = file_size > max_len ? max_len : file_size; + if (read(fd, data, read_len) != read_len) { + ESP_LOGE(TAG, "file read error"); + last_write_len = 0; + res = MTP_RESPONSE_INCOMPLETE_TRANSFER; + break; + } + if (file_size != st.st_size) { + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + } + file_size -= read_len; + last_write_len = file_size > 0 ? read_len : MTP_CONTAINER_HEAD_LEN + read_len; + + if (data == container->data) { + handle->write(handle->pipe_context, handle->buff, last_write_len); + data = container->data + max_len; + } else { + handle->write(handle->pipe_context, handle->buff + max_len, last_write_len); + memcpy(handle->buff, data + read_len - MTP_CONTAINER_HEAD_LEN, MTP_CONTAINER_HEAD_LEN); + data = container->data; + } + } + close(fd); + if (handle->flags & ESP_MTP_FLAG_ASYNC_WRITE) { + uint32_t notify_value; + xTaskNotifyWait(0x0, ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } + check_usb_len_mps_and_send_end(handle, last_write_len); + container->trans_id = trans_id; + return res; +#else + uint32_t object_handle; + mtp_container_t *container = (mtp_container_t *)handle->buff; + object_handle = container->operation.get_object.object_handle; + return _get_object_common(handle, object_handle, 0x0, 0xFFFFFFFF, NULL); +#endif +} + +static mtp_response_code_t send_object_info(esp_mtp_handle_t handle) +{ + uint8_t *data; + uint32_t parent_handle; + uint32_t storage_id; + mtp_container_t *container = (mtp_container_t *)handle->buff; + + storage_id = container->operation.send_object_info.storage_id; + parent_handle = container->operation.send_object_info.parent_handle; + if (parent_handle == 0xFFFFFFFF) { + parent_handle = 0; + } + + int len; + len = handle->read(handle->pipe_context, handle->buff, handle->buffer_size); + if (handle->flags & ESP_MTP_FLAG_ASYNC_READ) { + uint32_t notify_value; + do { + xTaskNotifyWait(0x0, ASYNC_READ_NOTIFY_BIT | ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } while (!(notify_value & ASYNC_READ_NOTIFY_BIT)); + len = handle->async_read_len; + } + + data = container->data; + //Skip No Use StorageID + data += sizeof(uint32_t); + + //ObjectFormat Code + mtp_object_format_code_t object_format; + object_format = *(mtp_object_format_code_t *)data; + data += sizeof(mtp_object_format_code_t); + + //Skip No Use Protection Status + data += sizeof(uint16_t); + + uint32_t file_size; + file_size = *(uint32_t *)data; //Object Compressed Size + data += sizeof(uint32_t); + + //Skip No Use Thumb Format + data += sizeof(uint16_t); + + //Skip No Use Thumb Compressed Size + data += sizeof(uint32_t); + + //Skip No Use Thumb Pix Width + data += sizeof(uint32_t); + + //Skip No Use Thumb Pix Height + data += sizeof(uint32_t); + + //Skip No Use Image Pix Width + data += sizeof(uint32_t); + + //Skip No Use Image Pix Height + data += sizeof(uint32_t); + + //Skip No Use Image Bit Depth + data += sizeof(uint32_t); + + //Skip No Use Parent Object + data += sizeof(uint32_t); + + //Ignore Association Type + data += sizeof(mtp_association_type_t); + + //Ignore Association Desc + data += sizeof(uint32_t); + + //Skip No Use Sequence Number + data += sizeof(uint32_t); + + //Filename + uint8_t str_len; + str_len = *data; + data += sizeof(uint8_t); + char filename[256]; + uint8_t temp_len; + esp_mtp_utf16_to_utf8((char *)data, (uint8_t *)filename, &temp_len); + ESP_LOGW(TAG, "%s %s", __FUNCTION__, filename); + + data += (str_len * 2); + + //Ignore Date Created "YYYYMMDDThhmmss.s" + str_len = *data; + data += sizeof(uint8_t); + data += (str_len * 2); + + //Ignore Date Modified "YYYYMMDDThhmmss.s" + str_len = *data; + data += sizeof(uint8_t); + data += (str_len * 2); + + //Skip No Use Keywords + str_len = *data; + data += sizeof(uint8_t); + data += (str_len * 2); + + if (parent_handle == 0) { + if (storage_id == 0) { + return MTP_RESPONSE_INVALID_PARAMETER; + } + } + //todo + strcpy((char *)container->data, "/sdcard"); + data = container->data + strlen((char *)container->data); + const esp_mtp_file_entry_t *entry = NULL; + if (parent_handle != 0) { + entry = esp_mtp_file_list_find(&handle->handle_list, parent_handle, (char *)data, handle->buff + handle->buffer_size - data); + if (entry == NULL) { + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + data += strlen((char *)data); + } + + if (handle->buff + handle->buffer_size - data < strlen(filename) + 2) { + return MTP_RESPONSE_ACCESS_DENIED; + } + *data = '/'; + data++; + strcpy((char *)data, filename); + ESP_LOGW(TAG, "%s %s", __FUNCTION__, (char *)container->data); + + int fd = -1; + if (object_format != MTP_OBJECT_FORMAT_ASSOCIATION) { + fd = open((char *)container->data, O_WRONLY | O_CREAT | O_EXCL); + if (fd < 0) { + return MTP_RESPONSE_ACCESS_DENIED; + } + + } else { + if (mkdir((char *)container->data, 755) != 0) { + return MTP_RESPONSE_ACCESS_DENIED; + } + } + + mtp_response_code_t req = MTP_RESPONSE_MAX; + + container->response.send_object_info.storage_id = storage_id; + container->response.send_object_info.parent_handle = parent_handle; + if (container->response.send_object_info.parent_handle == 0) { + container->response.send_object_info.parent_handle = 0xFFFFFFFF; + } + container->response.send_object_info.object_handle = esp_mtp_file_list_add(&handle->handle_list, 0x00010001, parent_handle, filename); + if (container->response.send_object_info.object_handle == 0) { + ESP_LOGW(TAG, "add file list fail"); + req = MTP_RESPONSE_ACCESS_DENIED; + goto exit; + } + container->len = MTP_CONTAINER_HEAD_LEN + 12; + container->type = MTP_CONTAINER_RESPONSE; + container->res = MTP_RESPONSE_OK; + handle->write(handle->pipe_context, handle->buff, container->len); + + if (fd >= 0) { + + if (file_size) { + uint32_t max_len; + uint32_t read_len; + uint32_t last_write_len = 0; + + max_len = handle->buffer_size - MTP_CONTAINER_HEAD_LEN; + max_len = max_len / 2; + max_len = max_len & (~0x1ff); //512对齐 + + len = handle->read(handle->pipe_context, handle->buff, max_len); + if (handle->flags & ESP_MTP_FLAG_ASYNC_READ) { + uint32_t notify_value; + do { + xTaskNotifyWait(0x0, ASYNC_READ_NOTIFY_BIT | ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } while (!(notify_value & ASYNC_READ_NOTIFY_BIT)); + len = handle->async_read_len; + } + ESP_LOGI(TAG, "%d recv %d", __LINE__, len); + + if (container->type != MTP_CONTAINER_OPERATION || container->opt != MTP_OPERATION_SEND_OBJECT) { + req = MTP_RESPONSE_PARAMETER_NOT_SUPPORTED; + goto exit; + } + + //DWC2 read len 为非 MPS 倍数时,如果主机发送大于 read len 的数据会产生错误 + //len = handle->read(handle->pipe_context, handle->buff, MTP_CONTAINER_HEAD_LEN + max_len); + len = handle->read(handle->pipe_context, handle->buff, max_len); + if (handle->flags & ESP_MTP_FLAG_ASYNC_READ) { + uint32_t notify_value; + do { + xTaskNotifyWait(0x0, ASYNC_READ_NOTIFY_BIT | ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } while (!(notify_value & ASYNC_READ_NOTIFY_BIT)); + len = handle->async_read_len; + } + ESP_LOGI(TAG, "%d recv %d", __LINE__, len); + + if (container->len != MTP_CONTAINER_HEAD_LEN + file_size || container->type != MTP_CONTAINER_DATA || container->opt != MTP_OPERATION_SEND_OBJECT) { + req = MTP_RESPONSE_PARAMETER_NOT_SUPPORTED; + goto exit; + } + last_write_len = len - MTP_CONTAINER_HEAD_LEN; + data = container->data; + while (1) { + file_size -= last_write_len; + if (file_size) { + read_len = file_size > max_len ? max_len : file_size; + if (data == container->data) { + len = handle->read(handle->pipe_context, container->data + max_len, max_len); + } else { + len = handle->read(handle->pipe_context, container->data, max_len); + } + } + if (write(fd, data, last_write_len) != last_write_len) { + req = MTP_RESPONSE_ACCESS_DENIED; + goto exit; + } + if (file_size == 0) { + break; + } + if (handle->flags & ESP_MTP_FLAG_ASYNC_READ) { + uint32_t notify_value; + do { + xTaskNotifyWait(0x0, ASYNC_READ_NOTIFY_BIT | ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } while (!(notify_value & ASYNC_READ_NOTIFY_BIT)); + len = handle->async_read_len; + } + if (len != read_len) { + req = MTP_RESPONSE_PARAMETER_NOT_SUPPORTED; + goto exit; + } + last_write_len = len; + if (data == container->data) { + data = container->data + max_len; + } else { + data = container->data; + } + } + req = MTP_RESPONSE_OK; + ESP_LOGI(TAG, "recv file ok"); + } + } +exit: + if (fd >= 0) { + close(fd); + } + return req; +} + +static mtp_response_code_t send_object(esp_mtp_handle_t handle) +{ + int len; + mtp_container_t *container = (mtp_container_t *)handle->buff; + len = handle->read(handle->pipe_context, handle->buff, handle->buffer_size); + if (handle->flags & ESP_MTP_FLAG_ASYNC_READ) { + uint32_t notify_value; + do { + xTaskNotifyWait(0x0, ASYNC_READ_NOTIFY_BIT | ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } while (!(notify_value & ASYNC_READ_NOTIFY_BIT)); + len = handle->async_read_len; + } + // 有文件内容接收已在 send_object_info 中进行处理,此处仅处理无内容接收的包 + if (len != container->len || container->len != MTP_CONTAINER_HEAD_LEN || container->type != MTP_CONTAINER_DATA || container->opt != MTP_OPERATION_SEND_OBJECT) { + return MTP_RESPONSE_PARAMETER_NOT_SUPPORTED; + } + return MTP_RESPONSE_OK; +} + +static void delete_dir(char *path, uint32_t path_len, uint32_t max_len) +{ + DIR *dir; + struct dirent *file; + char *data; + dir = opendir(path); + if (dir == NULL) { + ESP_LOGW(TAG, "Failed to open dir for reading:%s", path); + return; + } + data = path + path_len; + *data = '/'; + data++; + while ((file = readdir(dir)) != NULL) { + if (strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0) { + continue; + } + uint32_t len = strlen(file->d_name); + if (path_len + len + 2 > max_len) { + ESP_LOGW(TAG, "path too long:%s", file->d_name); + continue; + } + strcpy(data, file->d_name); + if (file->d_type == DT_DIR) { + delete_dir(path, path_len + len + 1, max_len); + } else { + unlink(path); + } + } + data--; + *data = '\0'; + closedir(dir); + rmdir(path); +} + +static mtp_response_code_t delete_object(esp_mtp_handle_t handle) +{ + uint8_t *data; + uint32_t object_handle; + mtp_container_t *container = (mtp_container_t *)handle->buff; + + object_handle = container->operation.delete_object.object_handle; + if (object_handle == 0x0) { + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + if (object_handle == 0xFFFFFFFF && container->operation.delete_object.object_format_code != 0x0) { + return MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED; + } + strcpy((char *)container->data, "/sdcard"); + data = container->data + strlen((char *)container->data); + const esp_mtp_file_entry_t *entry = NULL; + if (object_handle != 0) { + entry = esp_mtp_file_list_find(&handle->handle_list, object_handle, (char *)data, handle->buff + handle->buffer_size - data); + if (entry == NULL) { + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + data += strlen((char *)data); + } + + struct stat st; + if (stat((char *)container->data, &st) != 0) { + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + if (S_ISDIR(st.st_mode)) { + delete_dir((char *)container->data, data - container->data, handle->buffer_size - MTP_CONTAINER_HEAD_LEN); + } else { + unlink((char *)container->data); + } + + return MTP_RESPONSE_OK; +} + +static mtp_response_code_t get_partial_object(esp_mtp_handle_t handle) +{ + mtp_response_code_t res; + mtp_container_t *container = (mtp_container_t *)handle->buff; + uint32_t len = 0; + res = _get_object_common(handle, container->operation.get_partial_object.object_handle, container->operation.get_partial_object.offset, container->operation.get_partial_object.max_bytes, &len); + if (res != MTP_RESPONSE_OK) { + return res; + } + container->response.get_partial_object.actual_bytes = len; + container->len = MTP_CONTAINER_HEAD_LEN + 4; + container->type = MTP_CONTAINER_RESPONSE; + container->res = MTP_RESPONSE_OK; + handle->write(handle->pipe_context, handle->buff, container->len); + return MTP_RESPONSE_MAX; +} + +static void esp_mtp_task(void *args) +{ + int len; + esp_mtp_handle_t handle = (esp_mtp_handle_t)args; + mtp_container_t *container; + container = (mtp_container_t *)handle->buff; + +wait: + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + while (1) { + len = handle->read(handle->pipe_context, handle->buff, handle->buffer_size); + if (handle->flags & ESP_MTP_FLAG_ASYNC_READ) { + uint32_t notify_value; + do { + xTaskNotifyWait(0x0, ASYNC_READ_NOTIFY_BIT | ASYNC_WRITE_NOTIFY_BIT, ¬ify_value, portMAX_DELAY); + } while (!(notify_value & ASYNC_READ_NOTIFY_BIT)); + len = handle->async_read_len; + } + if (len < 0) { + break; + } + if (len == 0) { + goto wait; + } + if (len != container->len || container->type != MTP_CONTAINER_OPERATION) { + continue; + } + + mtp_response_code_t res = MTP_RESPONSE_MAX; + switch (container->opt) { + case MTP_OPERATION_OPEN_SESSION: + res = open_session(handle); + break; + case MTP_OPERATION_GET_THUMB: + res = get_thumb(handle); + break; + case MTP_OPERATION_GET_DEVICE_INFO: + res = get_device_info(handle); + break; + case MTP_OPERATION_GET_STORAGE_IDS: + res = get_storage_ids(handle); + break; + case MTP_OPERATION_GET_STORAGE_INFO: + res = get_storage_info(handle); + break; + case MTP_OPERATION_GET_OBJECT_HANDLES: + res = get_object_handles(handle); + break; + case MTP_OPERATION_GET_OBJECT_INFO: + res = get_object_info(handle); + break; + case MTP_OPERATION_GET_OBJECT: + res = get_object(handle); + break; + case MTP_OPERATION_SEND_OBJECT_INFO: + res = send_object_info(handle); + break; + case MTP_OPERATION_SEND_OBJECT: + res = send_object(handle); + break; + case MTP_OPERATION_DELETE_OBJECT: + res = delete_object(handle); + break; + case MTP_OPERATION_GET_PARTIAL_OBJECT: + res = get_partial_object(handle); + break; + default: + ESP_LOGW(TAG, "Undefine handle 0x%"PRIx16"(len:%"PRIu32")", container->opt, container->len); + res = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; + break; + } + if (res != MTP_RESPONSE_MAX) { + container->len = MTP_CONTAINER_HEAD_LEN; + container->type = MTP_CONTAINER_RESPONSE; + container->res = res; + handle->write(handle->pipe_context, handle->buff, container->len); + // 长度不会达到 MPS,无需检查 + // check_usb_len_mps_and_send_end(handle, container->len); + } + + } + ESP_LOGW(TAG, "MTP task exit"); + free(handle); + vTaskDelete(NULL); +} + +esp_mtp_handle_t esp_mtp_init(const esp_mtp_config_t *config) +{ + esp_mtp_handle_t handle; + uint32_t buffer_size; + buffer_size = config->buffer_size; + if (buffer_size < 1024) { + buffer_size = 1024; + } + + handle = malloc(sizeof(esp_mtp_t) + MTP_CONTAINER_HEAD_LEN + buffer_size); + + esp_mtp_file_list_init(&handle->handle_list); + + handle->pipe_context = config->pipe_context; + handle->read = config->read; + handle->write = config->write; + handle->flags = config->flags; + handle->buffer_size = MTP_CONTAINER_HEAD_LEN + buffer_size; + if (xTaskCreate(esp_mtp_task, "esp_mtp_task", 4096, + handle, 5, &handle->task_hdl) != pdTRUE) { + goto _exit; + } + return handle; +_exit: + free(handle); + return NULL; +} + +void esp_mtp_read_async_cb(esp_mtp_handle_t handle, int len) +{ + BaseType_t high_task_wakeup; + high_task_wakeup = pdFALSE; + handle->async_read_len = len; + xTaskNotifyFromISR(handle->task_hdl, ASYNC_READ_NOTIFY_BIT, eSetBits, &high_task_wakeup); + if (high_task_wakeup == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +void esp_mtp_write_async_cb(esp_mtp_handle_t handle, int len) +{ + // USB_LOG_DBG("send:%d\r\n", len); + BaseType_t high_task_wakeup = pdFALSE; + xTaskNotifyFromISR(handle->task_hdl, ASYNC_WRITE_NOTIFY_BIT, eSetBits, &high_task_wakeup); + if (high_task_wakeup == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +TaskHandle_t esp_mtp_get_task_handle(esp_mtp_handle_t handle) +{ + return handle->task_hdl; +} \ No newline at end of file diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c new file mode 100644 index 0000000..acada71 --- /dev/null +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2024, udoudou + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_mtp_helper.h" + +#include "string.h" +#include +#include "inttypes.h" + +uint8_t *esp_mtp_utf8_to_utf16(const char *utf8, uint8_t *out, uint8_t *len) +{ + uint16_t *out_start = (uint16_t *)out; + uint16_t *out_end = out_start; + while (*utf8 != '\0') { + uint16_t temp; + temp = *(utf8++); + if (temp & 0x80) { + uint8_t first_byte = temp; + if ((first_byte & 0xE0) == 0xC0) { + temp = (first_byte & 0x1F) << 6; + first_byte = *(utf8++); + if ((first_byte & 0xC0) != 0x80) { + break; + } + temp |= (first_byte & 0x3F); + } else if ((first_byte & 0xF0) == 0xE0) { + temp = (first_byte & 0x0F) << 12; + first_byte = *(utf8++); + if ((first_byte & 0xC0) != 0x80) { + break; + } + temp |= ((first_byte & 0x3F) << 6); + first_byte = *(utf8++); + if ((first_byte & 0xC0) != 0x80) { + break; + } + temp |= (first_byte & 0x3F); + } else { + break; + } + } + *(out_end++) = temp; + } + *(out_end++) = 0x0000; + *len = out_end - out_start; + return (uint8_t *)out_end; +} + +uint8_t *esp_mtp_utf16_to_utf8(const char *utf16, uint8_t *out, uint8_t *len) +{ + uint16_t *utf16_ptr = (uint16_t *)utf16; + uint8_t *out_start = out; + while (*utf16_ptr != 0x0) { + uint16_t temp = *(utf16_ptr++); + if (temp <= 0x7F) { + *(out++) = temp; + continue; + } else if (temp <= 0x7FF) { + *(out++) = (temp >> 6) | 0xC0; + *(out++) = (temp & 0x3F) | 0x80; + continue; + } + *(out++) = (temp >> 12) | 0xE0; + *(out++) = ((temp >> 6) & 0x3F) | 0x80; + *(out++) = (temp & 0x3F) | 0x80; + } + *(out++) = 0x00; + *len = (out - out_start); + return out; +} + +void esp_mtp_file_list_init(esp_mtp_file_handle_list_t *file_list) +{ + memset(file_list, 0, sizeof(esp_mtp_file_handle_list_t)); +} + +uint32_t esp_mtp_file_list_add(esp_mtp_file_handle_list_t *file_list, uint32_t storage_id, uint32_t parent, const char *name) +{ + esp_mtp_file_list_t *list; + list = &file_list->list; + for (uint32_t i = 1; i <= file_list->count / MTP_FILE_LIST_SIZE; i++) { + if (list->next == NULL) { + list->next = (esp_mtp_file_list_t *)calloc(1, sizeof(esp_mtp_file_list_t)); + if (list->next == NULL) { + return 0; + } + } + list = list->next; + } + list->entry_list[file_list->count % MTP_FILE_LIST_SIZE].name = (char *)malloc(strlen(name) + 1); + if (list->entry_list[file_list->count % MTP_FILE_LIST_SIZE].name == NULL) { + return 0; + } + strcpy(list->entry_list[file_list->count % MTP_FILE_LIST_SIZE].name, name); + list->entry_list[file_list->count % MTP_FILE_LIST_SIZE].storage_id = storage_id; + list->entry_list[file_list->count % MTP_FILE_LIST_SIZE].parent = parent; + file_list->count++; + // printf("add hande: %"PRIu32"(%"PRIu32")\n", file_list->count, parent); + return file_list->count; +} + +const esp_mtp_file_entry_t *esp_mtp_file_list_find(esp_mtp_file_handle_list_t *file_list, uint32_t handle, char *path, uint32_t max_len) +{ + esp_mtp_file_entry_t *entry; + esp_mtp_file_list_t *list; + uint32_t path_len = 0; + + if (handle > file_list->count) { + return NULL; + } + handle = handle - 1; + list = &file_list->list; + for (uint32_t i = 1; i <= handle / MTP_FILE_LIST_SIZE; i++) { + list = list->next; + } + entry = &list->entry_list[handle % MTP_FILE_LIST_SIZE]; + if (entry->name == NULL) { + return NULL; + } + // printf("find: %"PRIu32"(%"PRIu16")\n", handle + 1, entry->parent); + if (entry->parent != 0) { + if (esp_mtp_file_list_find(file_list, entry->parent, path, max_len) == NULL) { + return NULL; + } + path_len = strlen(path); + } + + if (path_len + strlen(entry->name) + 2 > max_len) { + return NULL; + } + path[path_len++] = '/'; + strcpy(path + path_len, entry->name); + return entry; +} + +uint8_t *esp_mtp_file_list_fill_handle_array(esp_mtp_file_handle_list_t *file_list, uint32_t parent, uint8_t *out, uint32_t *len) +{ + uint32_t max_count; + uint32_t count = 0; + esp_mtp_file_list_t *list; + + if (parent < file_list->count) { + list = &file_list->list; + for (uint32_t i = 1; i <= parent / MTP_FILE_LIST_SIZE; i++) { + list = list->next; + } + max_count = *len / sizeof(uint32_t); + for (uint32_t i = parent; i < file_list->count; i++) { + if (list->entry_list[i % MTP_FILE_LIST_SIZE].parent == parent) { + *(uint32_t *)out = i + 1; + out += sizeof(uint32_t); + count++; + if (count == max_count) { + break;; + } + } + } + } + + *len = count; + return out; +} \ No newline at end of file diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp.h b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp.h new file mode 100644 index 0000000..5da4864 --- /dev/null +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, udoudou + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "esp_err.h" + +typedef struct esp_mtp *esp_mtp_handle_t; + +typedef enum { + ESP_MTP_FLAG_NONE = 0, + ESP_MTP_FLAG_USB_FS = 1 << 0, + ESP_MTP_FLAG_USB_HS = 1 << 1, + ESP_MTP_FLAG_ASYNC_READ = 1 << 2, + ESP_MTP_FLAG_ASYNC_WRITE = 1 << 3, + ESP_MTP_FLAG_MAX = 0xFFFFFFFF, +} __attribute__((packed)) esp_mtp_flags_t; + +typedef struct { + void *pipe_context; + int (*read)(void *pipe_context, uint8_t *buffer, int len); + int (*write)(void *pipe_context, const uint8_t *buffer, int len); + esp_mtp_flags_t flags; + uint32_t buffer_size; +}esp_mtp_config_t; + +esp_mtp_handle_t esp_mtp_init(const esp_mtp_config_t *config); + +void esp_mtp_read_async_cb(esp_mtp_handle_t handle, int len); + +void esp_mtp_write_async_cb(esp_mtp_handle_t handle, int len); + +TaskHandle_t esp_mtp_get_task_handle(esp_mtp_handle_t handle); diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_def.h b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_def.h new file mode 100644 index 0000000..c884586 --- /dev/null +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_def.h @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2024, udoudou + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define MTP_STANDARD_VERSION 100 +#define MTP_VENDOR_EXTN_ID 0x06 +#define MTP_VENDOR_EXTN_VERSION 100 +#define MTP_VENDOR_EXTENSIONDESC_CHAR "microsoft.com: 1.0; android.com: 1.0;" + + /* + * Functional Modes: + */ +typedef enum { + MTP_FUNCTIONAL_STANDARD = 0x0000, + MTP_FUNCTIONAL_SLEEP = 0x0001, + MTP_FUNCTIONAL_MAX = 0xFFFF, +}__attribute__((packed)) mtp_functional_mode_t; + +/* MTP Storage Type */ +typedef enum { + MTP_STORAGE_UNDEFINED = 0x0000, + MTP_STORAGE_FIXED_ROM = 0x0001, + MTP_STORAGE_REMOVABLE_ROM = 0x0002, + MTP_STORAGE_FIXED_RAM = 0x0003, + MTP_STORAGE_REMOVABLE_RAM = 0x0004, + MTP_STORAGE_MAX = 0xFFFF, +}__attribute__((packed)) mtp_storage_type_t; + +/* MTP File System Type */ +typedef enum { + MTP_FILE_SYSTEM_UNDEFINED = 0x0000, + MTP_FILE_SYSTEM_GENERIC_FLAT = 0x0001, + MTP_FILE_SYSTEM_GENERIC_HIERARCH = 0x0002, + MTP_FILE_SYSTEM_DCF = 0x0003, + MTP_FILE_SYSTEM_MAX = 0xFFFF, +}__attribute__((packed)) mtp_file_system_type_t; + +/* MTP Class Requests */ +#define MTP_REQUEST_CANCEL 0x64U +#define MTP_REQUEST_GET_EXT_EVENT_DATA 0x65U +#define MTP_REQUEST_RESET 0x66U +#define MTP_REQUEST_GET_DEVICE_STATUS 0x67U + +/* MTP Container Types */ +typedef enum { + MTP_CONTAINER_UNDEFINED = 0, + MTP_CONTAINER_OPERATION = 1, + MTP_CONTAINER_DATA = 2, + MTP_CONTAINER_RESPONSE = 3, + MTP_CONTAINER_EVENT = 4, + MTP_CONTAINER_MAX = 0xFFFF, +} __attribute__((packed)) mtp_container_type_t; + +/* MTP Access Capability */ +typedef enum { + MTP_ACCESS_CAP_RW = 0x0000, + MTP_ACCESS_CAP_RO_WITHOUT_DEL = 0x0001, + MTP_ACCESS_CAP_RO_WITH_DEL = 0x0002, + MTP_ACCESS_CAP_MAX = 0xFFFF, +} __attribute__((packed)) mtp_access_cap_t; + +/* + * MTP Class Specification Revision 1.1 + */ + + /* MTP Standard Data Types Supported */ +typedef enum { + MTP_DATA_TYPE_UNDEF = 0x0000, + MTP_DATA_TYPE_INT8 = 0x0001, + MTP_DATA_TYPE_UINT8 = 0x0002, + MTP_DATA_TYPE_INT16 = 0x0003, + MTP_DATA_TYPE_UINT16 = 0x0004, + MTP_DATA_TYPE_INT32 = 0x0005, + MTP_DATA_TYPE_UINT32 = 0x0006, + MTP_DATA_TYPE_INT64 = 0x0007, + MTP_DATA_TYPE_UINT64 = 0x0008, + MTP_DATA_TYPE_INT128 = 0x0009, + MTP_DATA_TYPE_UINT128 = 0x000A, +#define MTP_DATA_TYPE_ARRAY BIT14 + MTP_DATA_TYPE_AINT8 = 0x4001, + MTP_DATA_TYPE_AUINT8 = 0x4002, + MTP_DATA_TYPE_AINT16 = 0x4003, + MTP_DATA_TYPE_AUINT16 = 0x4004, + MTP_DATA_TYPE_AINT32 = 0x4005, + MTP_DATA_TYPE_AUINT32 = 0x4006, + MTP_DATA_TYPE_AINT64 = 0x4007, + MTP_DATA_TYPE_AUINT64 = 0x4008, + MTP_DATA_TYPE_AINT128 = 0x4009, + MTP_DATA_TYPE_AUINT128 = 0x400A, + MTP_DATA_TYPE_STR = 0xFFFF, +} __attribute__((packed)) mtp_data_type_t; + +/* Appendix A - Object Formats */ + +/* MTP Object Format Codes */ +typedef enum { + MTP_OBJECT_FORMAT_UNDEFINED = 0x3000, + MTP_OBJECT_FORMAT_ASSOCIATION = 0x3001, + MTP_OBJECT_FORMAT_SCRIPT = 0x3002, + MTP_OBJECT_FORMAT_EXECUTABLE = 0x3003, + MTP_OBJECT_FORMAT_TEXT = 0x3004, + MTP_OBJECT_FORMAT_HTML = 0x3005, + MTP_OBJECT_FORMAT_DPOF = 0x3006, + MTP_OBJECT_FORMAT_AIFF = 0x3007, + MTP_OBJECT_FORMAT_WAVE = 0x3008, + MTP_OBJECT_FORMAT_MP3 = 0x3009, + MTP_OBJECT_FORMAT_AVI = 0x300A, + MTP_OBJECT_FORMAT_MPEG = 0x300B, + MTP_OBJECT_FORMAT_ASF = 0x300C, + MTP_OBJECT_FORMAT_UNDEFINED_IMAGE = 0x3800, + MTP_OBJECT_FORMAT_EXIF_JPEG = 0x3801, + MTP_OBJECT_FORMAT_TIFF_EP = 0x3802, + MTP_OBJECT_FORMAT_FLASHPIX = 0x3803, + MTP_OBJECT_FORMAT_BMP = 0x3804, + MTP_OBJECT_FORMAT_CIFF = 0x3805, + MTP_OBJECT_FORMAT_UNDEFINED_RESERVED0 = 0x3806, + MTP_OBJECT_FORMAT_GIF = 0x3807, + MTP_OBJECT_FORMAT_JFIF = 0x3808, + MTP_OBJECT_FORMAT_CD = 0x3809, + MTP_OBJECT_FORMAT_PICT = 0x380A, + MTP_OBJECT_FORMAT_PNG = 0x380B, + MTP_OBJECT_FORMAT_UNDEFINED_RESERVED1 = 0x380C, + MTP_OBJECT_FORMAT_TIFF = 0x380D, + MTP_OBJECT_FORMAT_TIFF_IT = 0x380E, + MTP_OBJECT_FORMAT_JP2 = 0x380F, + MTP_OBJECT_FORMAT_JPX = 0x3810, + MTP_OBJECT_FORMAT_UNDEFINED_FIRMWARE = 0xB802, + MTP_OBJECT_FORMAT_WINDOWS_IMAGE_FORMAT = 0xB881, + MTP_OBJECT_FORMAT_WBMP = 0xB803, + MTP_OBJECT_FORMAT_JPEG_XR = 0xB804, + MTP_OBJECT_FORMAT_UNDEFINED_AUDIO = 0xB900, + MTP_OBJECT_FORMAT_WMA = 0xB901, + MTP_OBJECT_FORMAT_OGG = 0xB902, + MTP_OBJECT_FORMAT_AAC = 0xB903, + MTP_OBJECT_FORMAT_AUDIBLE = 0xB904, + MTP_OBJECT_FORMAT_FLAC = 0xB906, + MTP_OBJECT_FORMAT_QCELP = 0xB907, + MTP_OBJECT_FORMAT_AMR = 0xB908, + MTP_OBJECT_FORMAT_UNDEFINED_VIDEO = 0xB980, + MTP_OBJECT_FORMAT_WMV = 0xB981, + MTP_OBJECT_FORMAT_MP4_CONTAINER = 0xB982, + MTP_OBJECT_FORMAT_MP2 = 0xB983, + MTP_OBJECT_FORMAT_3GP_CONTAINER = 0xB984, + MTP_OBJECT_FORMAT_3G2 = 0xB985, + MTP_OBJECT_FORMAT_AVCHD = 0xB986, + MTP_OBJECT_FORMAT_ATSC_TS = 0xB987, + MTP_OBJECT_FORMAT_DVB_TS = 0xB988, + MTP_OBJECT_FORMAT_UNDEFINED_COLLECTION = 0xBA00, + MTP_OBJECT_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM = 0xBA01, + MTP_OBJECT_FORMAT_ABSTRACT_IMAGE_ALBUM = 0xBA02, + MTP_OBJECT_FORMAT_ABSTRACT_AUDIO_ALBUM = 0xBA03, + MTP_OBJECT_FORMAT_ABSTRACT_VIDEO_ALBUM = 0xBA04, + MTP_OBJECT_FORMAT_ABSTRACT_AUDIO_VIDEO_PLAYLIST = 0xBA05, + MTP_OBJECT_FORMAT_ABSTRACT_CONTACT_GROUP = 0xBA06, + MTP_OBJECT_FORMAT_ABSTRACT_MESSAGE_FOLDER = 0xBA07, + MTP_OBJECT_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION = 0xBA08, + MTP_OBJECT_FORMAT_ABSTRACT_AUDIO_PLAYLIST = 0xBA09, + MTP_OBJECT_FORMAT_ABSTRACT_VIDEO_PLAYLIST = 0xBA0A, + MTP_OBJECT_FORMAT_ABSTRACT_MEDIACAST = 0xBA0B, + MTP_OBJECT_FORMAT_WPL_PLAYLIST = 0xBA10, + MTP_OBJECT_FORMAT_M3U_PLAYLIST = 0xBA11, + MTP_OBJECT_FORMAT_MPL_PLAYLIST = 0xBA12, + MTP_OBJECT_FORMAT_ASX_PLAYLIST = 0xBA13, + MTP_OBJECT_FORMAT_PLS_PLAYLIST = 0xBA14, + MTP_OBJECT_FORMAT_UNDEFINED_DOCUMENT = 0xBA80, + MTP_OBJECT_FORMAT_ABSTRACT_DOCUMENT = 0xBA81, + MTP_OBJECT_FORMAT_XML_DOCUMENT = 0xBA82, + MTP_OBJECT_FORMAT_MICROSOFT_WORD_DOCUMENT = 0xBA83, + MTP_OBJECT_FORMAT_MHT_COMPILED_HTML_DOCUMENT = 0xBA84, + MTP_OBJECT_FORMAT_MICROSOFT_EXCEL_SPREADSHEET = 0xBA85, + MTP_OBJECT_FORMAT_MICROSOFT_POWERPOINT_PRESEMTATION = 0xBA86, + MTP_OBJECT_FORMAT_UNDEFINED_MESSAGE = 0xBB00, + MTP_OBJECT_FORMAT_ABSTRACT_MESSAGE = 0xBB01, + MTP_OBJECT_FORMAT_UNDEFINED_BOOKMARK = 0xBB10, + MTP_OBJECT_FORMAT_ABSTRACT_BOOKMARK = 0xBB11, + MTP_OBJECT_FORMAT_UNDEFINED_APPOINTMENT = 0xBB20, + MTP_OBJECT_FORMAT_ABSTRACT_APPOINTMENT = 0xBB21, + MTP_OBJECT_FORMAT_VCALENDAR_1_0 = 0xBB22, + MTP_OBJECT_FORMAT_UNDEFINED_TASK = 0xBB40, + MTP_OBJECT_FORMAT_ABSTRACT_TASK = 0xBB41, + MTP_OBJECT_FORMAT_ICALENDAR = 0xBB42, + MTP_OBJECT_FORMAT_UNDEFINED_NOTE = 0xBB60, + MTP_OBJECT_FORMAT_ABSTRACT_NOTE = 0xBB61, + MTP_OBJECT_FORMAT_UNDEFINED_CONTACT = 0xBB80, + MTP_OBJECT_FORMAT_ABSTRACT_CONTACT = 0xBB81, + MTP_OBJECT_FORMAT_VCARD_2 = 0xBB82, + MTP_OBJECT_FORMAT_VCARD_3 = 0xBB83, + + MTP_OBJECT_FORMAT_MAX = 0xFFFF, +} __attribute__((packed)) mtp_object_format_code_t; + +/* Operations Codes */ +typedef enum { + /* Appendix D - Operations */ + //PTP Operation Code 0x1000-0x1FFF + MTP_OPERATION_GET_DEVICE_INFO = 0x1001, + MTP_OPERATION_OPEN_SESSION = 0x1002, + MTP_OPERATION_CLOSE_SESSION = 0x1003, + MTP_OPERATION_GET_STORAGE_IDS = 0x1004, + MTP_OPERATION_GET_STORAGE_INFO = 0x1005, + MTP_OPERATION_GET_NUM_OBJECTS = 0x1006, + MTP_OPERATION_GET_OBJECT_HANDLES = 0x1007, + MTP_OPERATION_GET_OBJECT_INFO = 0x1008, + MTP_OPERATION_GET_OBJECT = 0x1009, + MTP_OPERATION_GET_THUMB = 0x100A, + MTP_OPERATION_DELETE_OBJECT = 0x100B, + MTP_OPERATION_SEND_OBJECT_INFO = 0x100C, + MTP_OPERATION_SEND_OBJECT = 0x100D, + MTP_OPERATION_INITIATE_CAPTURE = 0x100E, + MTP_OPERATION_FORMAT_STORE = 0x100F, + MTP_OPERATION_RESET_DEVICE = 0x1010, + MTP_OPERATION_SELF_TEST = 0x1011, + MTP_OPERATION_SET_OBJECT_PROTECTION = 0x1012, + MTP_OPERATION_POWER_DOWN = 0x1013, + MTP_OPERATION_GET_DEVICE_PROP_DESC = 0x1014, + MTP_OPERATION_GET_DEVICE_PROP_VALUE = 0x1015, + MTP_OPERATION_SET_DEVICE_PROP_VALUE = 0x1016, + MTP_OPERATION_RESET_DEVICE_PROP_VALUE = 0x1017, + MTP_OPERATION_TERMINATE_OPEN_CAPTURE = 0x1018, + MTP_OPERATION_MOVE_OBJECT = 0x1019, + MTP_OPERATION_COPY_OBJECT = 0x101A, + MTP_OPERATION_GET_PARTIAL_OBJECT = 0x101B, + MTP_OPERATION_INITIATE_OPEN_CAPTURE = 0x101C, + //MTP Operation Code 0x9800-0x9FFF + MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED = 0x9801, + MTP_OPERATION_GET_OBJECT_PROP_DESC = 0x9802, + MTP_OPERATION_GET_OBJECT_PROP_VALUE = 0x9803, + MTP_OPERATION_SET_OBJECT_PROP_VALUE = 0x9804, + + MTP_OPERATION_GET_OBJECT_REFERENCES = 0x9810, + MTP_OPERATION_SET_OBJECT_REFERENCES = 0x9811, + MTP_OPERATION_SKIP = 0x9820, + + /* Appendix E - Enhanced Operations */ + MTP_OPERATION_GET_OBJECT_PROP_LIST = 0x9805, + MTP_OPERATION_SET_OBJECT_PROP_LIST = 0x9806, + MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC = 0x9807, + MTP_OPERATION_GET_SEND_OBJECT_PROP_LIST = 0x9808, + + MTP_OPERATION_MAX = 0xFFFF, +} __attribute__((packed)) mtp_operation_code_t; + +typedef struct { + union { + struct { + uint32_t parameter1; + uint32_t parameter2; + uint32_t parameter3; + uint32_t parameter4; + uint32_t parameter5; + }undefine; + + struct { + uint32_t object_handle; + }get_thumb; + + struct { + uint32_t storage_id; + }get_storage_info; + + struct { + uint32_t storage_id; + uint32_t object_format_code; //可选参数 + uint32_t parent_handle; //可选参数 + }get_object_handles; + + struct { + uint32_t object_handle; + }get_object_info; + + struct { + uint32_t object_handle; + }get_object; + + struct { + uint32_t storage_id; //可选参数 + uint32_t parent_handle; //可选参数 + }send_object_info; + + struct { + uint32_t object_handle; + uint32_t object_format_code; //可选参数 + }delete_object; + + struct { + uint32_t object_handle; + uint32_t offset; + uint32_t max_bytes; + }get_partial_object; + }; +}mtp_operation_container_t; + +/* Appendix F - Response */ + +/* MTP Response Codes */ +typedef enum { + //PTP Response Code 0x2000-0x2FFF + MTP_RESPONSE_UNDEFINED = 0x2000, + MTP_RESPONSE_OK = 0x2001, + MTP_RESPONSE_GENERAL_ERROR = 0x2002, + MTP_RESPONSE_SESSION_NOT_OPEN = 0x2003, + MTP_RESPONSE_INVALID_TRANSACTION_ID = 0x2004, + MTP_RESPONSE_OPERATION_NOT_SUPPORTED = 0x2005, + MTP_RESPONSE_PARAMETER_NOT_SUPPORTED = 0x2006, + MTP_RESPONSE_INCOMPLETE_TRANSFER = 0x2007, + MTP_RESPONSE_INVALID_STORAGE_ID = 0x2008, + MTP_RESPONSE_INVALID_OBJECT_HANDLE = 0x2009, + MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED = 0x200A, + MTP_RESPONSE_INVALID_OBJECT_FORMAT_CODE = 0x200B, + MTP_RESPONSE_STORE_FULL = 0x200C, + MTP_RESPONSE_OBJECT_WRITE_PROTECTED = 0x200D, + MTP_RESPONSE_STORE_READ_ONLY = 0x200E, + MTP_RESPONSE_ACCESS_DENIED = 0x200F, + MTP_RESPONSE_NO_THUMBNAIL_PRESENT = 0x2010, + MTP_RESPONSE_SELF_TEST_FAILED = 0x2011, + MTP_RESPONSE_PARTIAL_DELETION = 0x2012, + MTP_RESPONSE_STORE_NOT_AVAILABLE = 0x2013, + MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED = 0x2014, + MTP_RESPONSE_NO_VALID_OBJECT_INFO = 0x2015, + MTP_RESPONSE_INVALID_CODE_FORMAT = 0x2016, + MTP_RESPONSE_UNKNOWN_VENDOR_CODE = 0x2017, + MTP_RESPONSE_CAPTURE_ALREADY_TERMINATED = 0x2018, + MTP_RESPONSE_DEVICE_BUSY = 0x2019, + MTP_RESPONSE_INVALID_PARENT_OBJECT = 0x201A, + MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT = 0x201B, + MTP_RESPONSE_INVALID_DEVICE_PROP_VALUE = 0x201C, + MTP_RESPONSE_INVALID_PARAMETER = 0x201D, + MTP_RESPONSE_SESSION_ALREADY_OPEN = 0x201E, + MTP_RESPONSE_TRANSACTION_CANCELLED = 0x201F, + MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED = 0x2020, + //MTP Response Code 0xA800-0xAFFF + MTP_RESPONSE_INVALID_OBJECT_PROP_CODE = 0xA801, + MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT = 0xA802, + MTP_RESPONSE_INVALID_OBJECT_PROP_VALUE = 0xA803, + MTP_RESPONSE_INVALID_OBJECT_REFERENCE = 0xA804, + MTP_RESPONSE_GROUP_NOT_SUPPORTED = 0xA805, + MTP_RESPONSE_INVALID_DATASET = 0xA806, + MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED = 0xA807, + MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED = 0xA808, + MTP_RESPONSE_OBJECT_TOO_LARGE = 0xA809, + MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED = 0xA80A, + + MTP_RESPONSE_MAX = 0xFFFF, +} __attribute__((packed)) mtp_response_code_t; + +typedef struct { + union { + struct { + uint32_t parameter1; + uint32_t parameter2; + uint32_t parameter3; + uint32_t parameter4; + uint32_t parameter5; + }undefine; + + struct { + uint32_t storage_id; + uint32_t parent_handle;; + uint32_t object_handle; + }send_object_info; + + struct { + uint32_t actual_bytes; + }get_partial_object; + }; +}mtp_response_container_t; + + +typedef struct { + mtp_operation_code_t code; + uint32_t transaction_id; + uint8_t payload[0]; +}mtp_data_container_t; + +/* MTP Association Type */ +typedef enum { + MTP_ASSOCIATION_UNDEFINED = 0x0000, + MTP_ASSOCIATION_GENERIC_FOLDER = 0x0001, + MTP_ASSOCIATION_MAX = 0xFFFF, +}__attribute__((packed)) mtp_association_type_t; + +/* Appendix G. Events */ + +/* MTP Event Codes */ +typedef enum { + MTP_EVENT_UNDEFINED = 0x4000, + MTP_EVENT_CANCEL_TRANSACTION = 0x4001, + MTP_EVENT_OBJECT_ADDED = 0x4002, + MTP_EVENT_OBJECT_REMOVED = 0x4003, + MTP_EVENT_STORE_ADDED = 0x4004, + MTP_EVENT_STORE_REMOVED = 0x4005, + MTP_EVENT_DEVICE_PROP_CHANGED = 0x4006, + MTP_EVENT_OBJECT_INFO_CHANGED = 0x4007, + MTP_EVENT_DEVICE_INFO_CHANGED = 0x4008, + MTP_EVENT_REQUEST_OBJECT_TRANSFER = 0x4009, + MTP_EVENT_STORE_FULL = 0x400A, + MTP_EVENT_DEVICE_RESET = 0x400B, + MTP_EVENT_STORAGE_INFO_CHANGED = 0x400C, + MTP_EVENT_CAPTURE_COMPLETE = 0x400D, + MTP_EVENT_UNREPORTED_STATUS = 0x400E, + MTP_EVENT_OBJECT_PROP_CHANGED = 0xC801, + MTP_EVENT_DEVICE_PROP_DESC_CHANGED = 0xC802, + MTP_EVENT_OBJECT_REFERENCES_CHANGED = 0xC803, + MTP_EVENT_MAX = 0xFFFF, +}__attribute__((packed)) mtp_event_code_t; + +typedef struct { + uint32_t len; + mtp_container_type_t type; + union { + mtp_operation_code_t opt; + mtp_response_code_t res; + }; + uint32_t trans_id; + union { + mtp_operation_container_t operation; + uint8_t data[0]; + mtp_response_container_t response; + }; +}mtp_container_t; + +#define MTP_CONTAINER_HEAD_LEN offsetof(mtp_container_t, data) diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_helper.h b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_helper.h new file mode 100644 index 0000000..29593ce --- /dev/null +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_helper.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, udoudou + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#define MTP_FILE_LIST_SIZE 64 + +typedef struct { + uint16_t storage_id; + uint16_t parent; + char *name; +}esp_mtp_file_entry_t; + +typedef struct esp_mtp_file_list { + esp_mtp_file_entry_t entry_list[MTP_FILE_LIST_SIZE]; + struct esp_mtp_file_list *next; +}esp_mtp_file_list_t; + +typedef struct { + uint32_t count; + esp_mtp_file_list_t list; +}esp_mtp_file_handle_list_t; + +uint8_t *esp_mtp_utf8_to_utf16(const char *utf8, uint8_t *out, uint8_t *len); + +uint8_t *esp_mtp_utf16_to_utf8(const char *utf16, uint8_t *out, uint8_t *len); + +void esp_mtp_file_list_init(esp_mtp_file_handle_list_t *file_list); + +uint32_t esp_mtp_file_list_add(esp_mtp_file_handle_list_t *file_list, uint32_t storage_id, uint32_t parent, const char *name); + +const esp_mtp_file_entry_t *esp_mtp_file_list_find(esp_mtp_file_handle_list_t *file_list, uint32_t handle, char *path, uint32_t max_len); + +void esp_mtp_file_list_clean(esp_mtp_file_handle_list_t *file_list); + +uint8_t *esp_mtp_file_list_fill_handle_array(esp_mtp_file_handle_list_t *file_list, uint32_t parent, uint8_t *out, uint32_t *len); \ No newline at end of file diff --git a/examples/device/cherryusb_device_mtp/components/usb_mtp/CMakeLists.txt b/examples/device/cherryusb_device_mtp/components/usb_mtp/CMakeLists.txt new file mode 100644 index 0000000..4909309 --- /dev/null +++ b/examples/device/cherryusb_device_mtp/components/usb_mtp/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "usb_mtp.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES esp_cherryusb esp_mtp +) diff --git a/examples/device/cherryusb_device_mtp/components/usb_mtp/include/usb_mtp.h b/examples/device/cherryusb_device_mtp/components/usb_mtp/include/usb_mtp.h new file mode 100644 index 0000000..6551ddc --- /dev/null +++ b/examples/device/cherryusb_device_mtp/components/usb_mtp/include/usb_mtp.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, udoudou + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "usbd_core.h" + +#define USB_MTP_CLASS 0x06 + +#define USB_MTP_SUB_CLASS 0x01U +#define USB_MTP_PROTOCOL 0x01U + + /*Length of template descriptor: 23 bytes*/ +#define MTP_DESCRIPTOR_LEN (9 + 7 + 7 + 7) + +// clang-format off +#define MTP_DESCRIPTOR_INIT(bFirstInterface, out_ep, in_ep, int_ep, wMaxPacketSize, str_idx) \ + /* Interface */ \ + 0x09, /* bLength */ \ + USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ + bFirstInterface, /* bInterfaceNumber */ \ + 0x00, /* bAlternateSetting */ \ + 0x03, /* bNumEndpoints */ \ + USB_MTP_CLASS, /* bInterfaceClass */ \ + USB_MTP_SUB_CLASS, /* bInterfaceSubClass */ \ + USB_MTP_PROTOCOL, /* bInterfaceProtocol */ \ + str_idx, /* iInterface */ \ + 0x07, /* bLength */ \ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \ + out_ep, /* bEndpointAddress */ \ + 0x02, /* bmAttributes */ \ + WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \ + 0x00, /* bInterval */ \ + 0x07, /* bLength */ \ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \ + in_ep, /* bEndpointAddress */ \ + 0x02, /* bmAttributes */ \ + WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \ + 0x00, /* bInterval */ \ + 0x07, /* bLength */ \ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \ + int_ep, /* bEndpointAddress */ \ + 0x03, /* bmAttributes */ \ + 0x1c, 0x00, /* wMaxPacketSize */ \ + 0x06 /* bInterval */ +// clang-format on + + +struct usbd_interface *usbd_mtp_init_intf(struct usbd_interface *intf, + const uint8_t out_ep, + const uint8_t in_ep, + const uint8_t int_ep); \ No newline at end of file diff --git a/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c b/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c new file mode 100644 index 0000000..6f9fa2e --- /dev/null +++ b/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2024, udoudou + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "usb_mtp.h" +#include "esp_mtp_def.h" +#include "esp_mtp.h" + + /* Max USB packet size */ +#ifndef CONFIG_USB_HS +#define MTP_BULK_EP_MPS 64 +#else +#define MTP_BULK_EP_MPS 512 +#endif + +#define MTP_OUT_EP_IDX 0 +#define MTP_IN_EP_IDX 1 +#define MTP_INT_EP_IDX 2 + +/* Describe EndPoints configuration */ +static struct usbd_endpoint mtp_ep_data[3]; +static TaskHandle_t s_mtp_task_handle; +esp_mtp_handle_t s_handle; + +static int mtp_class_interface_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("MTP Class request: " + "bRequest 0x%02x\r\n", + setup->bRequest); + + switch (setup->bRequest) { + case MTP_REQUEST_CANCEL: + + break; + case MTP_REQUEST_GET_EXT_EVENT_DATA: + + break; + case MTP_REQUEST_RESET: + + break; + case MTP_REQUEST_GET_DEVICE_STATUS: + *(uint16_t *)(*data) = 0x08; + *(mtp_response_code_t *)(*data + 2) = MTP_RESPONSE_OK; + *len = 8; + break; + default: + USB_LOG_WRN("Unhandled MTP Class bRequest 0x%02x\r\n", setup->bRequest); + return -1; + } + + return 0; +} + +static void usbd_mtp_bulk_out(uint8_t ep, uint32_t nbytes) +{ + esp_mtp_read_async_cb(s_handle, nbytes); +} + +static void usbd_mtp_bulk_in(uint8_t ep, uint32_t nbytes) +{ + esp_mtp_write_async_cb(s_handle, nbytes); +} + +static void mtp_notify_handler(uint8_t event, void *arg) +{ + BaseType_t high_task_wakeup = pdFALSE; + switch (event) { + case USBD_EVENT_RESET: + break; + case USBD_EVENT_CONFIGURED: + USB_LOG_DBG("Start reading command\r\n"); + vTaskNotifyGiveFromISR(s_mtp_task_handle, &high_task_wakeup); + + break; + case USBD_EVENT_DISCONNECTED: + vTaskNotifyGiveFromISR(s_mtp_task_handle, &high_task_wakeup); + break; + default: + break; + } + if (high_task_wakeup == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +static int usb_write(void *pipe_context, const uint8_t *data, int data_size) +{ + usbd_ep_start_write(mtp_ep_data[MTP_IN_EP_IDX].ep_addr, data, data_size); + return data_size; +} + +static int usb_read(void *pipe_context, uint8_t *data, int data_size) +{ + usbd_ep_start_read(mtp_ep_data[MTP_OUT_EP_IDX].ep_addr, data, data_size); + return data_size; +} + +struct usbd_interface *usbd_mtp_init_intf(struct usbd_interface *intf, + const uint8_t out_ep, + const uint8_t in_ep, + const uint8_t int_ep) +{ + intf->class_interface_handler = mtp_class_interface_request_handler; + intf->class_endpoint_handler = NULL; + intf->vendor_handler = NULL; + intf->notify_handler = mtp_notify_handler; + + mtp_ep_data[MTP_OUT_EP_IDX].ep_addr = out_ep; + mtp_ep_data[MTP_OUT_EP_IDX].ep_cb = usbd_mtp_bulk_out; + mtp_ep_data[MTP_IN_EP_IDX].ep_addr = in_ep; + mtp_ep_data[MTP_IN_EP_IDX].ep_cb = usbd_mtp_bulk_in; + + //EVENT 通道 + mtp_ep_data[MTP_INT_EP_IDX].ep_addr = int_ep; + mtp_ep_data[MTP_INT_EP_IDX].ep_cb = NULL; + + usbd_add_endpoint(&mtp_ep_data[MTP_OUT_EP_IDX]); + usbd_add_endpoint(&mtp_ep_data[MTP_IN_EP_IDX]); + usbd_add_endpoint(&mtp_ep_data[MTP_INT_EP_IDX]); + + esp_mtp_config_t config = { + .read = usb_read, + .write = usb_write, + .flags = ESP_MTP_FLAG_ASYNC_READ | ESP_MTP_FLAG_ASYNC_WRITE, + .buffer_size = 4096, + }; +#ifndef CONFIG_USB_HS + config.flags |= ESP_MTP_FLAG_USB_FS; +#else + config.flags |= ESP_MTP_FLAG_USB_HS; +#endif + + s_handle = esp_mtp_init(&config); + + s_mtp_task_handle = esp_mtp_get_task_handle(s_handle); + + return intf; +} \ No newline at end of file diff --git a/examples/device/cherryusb_device_mtp/main/CMakeLists.txt b/examples/device/cherryusb_device_mtp/main/CMakeLists.txt new file mode 100644 index 0000000..7bade98 --- /dev/null +++ b/examples/device/cherryusb_device_mtp/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "device_mtp_main.c" "sd_card_example_main.c" + INCLUDE_DIRS "." +) diff --git a/examples/device/cherryusb_device_mtp/main/Kconfig.projbuild b/examples/device/cherryusb_device_mtp/main/Kconfig.projbuild new file mode 100644 index 0000000..2b2a81a --- /dev/null +++ b/examples/device/cherryusb_device_mtp/main/Kconfig.projbuild @@ -0,0 +1,57 @@ +menu "SD/MMC Example Configuration" + + config EXAMPLE_FORMAT_IF_MOUNT_FAILED + bool "Format the card if mount failed" + default n + help + If this config item is set, format_if_mount_failed will be set to true and the card will be formatted if + the mount has failed. + + choice EXAMPLE_SDMMC_BUS_WIDTH + prompt "SD/MMC bus width" + default EXAMPLE_SDMMC_BUS_WIDTH_4 + help + Select the bus width of SD or MMC interface. + Note that even if 1 line mode is used, D3 pin of the SD card must have a pull-up resistor connected. + Otherwise the card may enter SPI mode, the only way to recover from which is to cycle power to the card. + + config EXAMPLE_SDMMC_BUS_WIDTH_4 + bool "4 lines (D0 - D3)" + + config EXAMPLE_SDMMC_BUS_WIDTH_1 + bool "1 line (D0)" + endchoice + + if SOC_SDMMC_USE_GPIO_MATRIX + + config EXAMPLE_PIN_CMD + int "CMD GPIO number" + default 35 if IDF_TARGET_ESP32S3 + + config EXAMPLE_PIN_CLK + int "CLK GPIO number" + default 36 if IDF_TARGET_ESP32S3 + + config EXAMPLE_PIN_D0 + int "D0 GPIO number" + default 37 if IDF_TARGET_ESP32S3 + + if EXAMPLE_SDMMC_BUS_WIDTH_4 + + config EXAMPLE_PIN_D1 + int "D1 GPIO number" + default 38 if IDF_TARGET_ESP32S3 + + config EXAMPLE_PIN_D2 + int "D2 GPIO number" + default 33 if IDF_TARGET_ESP32S3 + + config EXAMPLE_PIN_D3 + int "D3 GPIO number" + default 34 if IDF_TARGET_ESP32S3 + + endif # EXAMPLE_SDMMC_BUS_WIDTH_4 + + endif # SOC_SDMMC_USE_GPIO_MATRIX + +endmenu diff --git a/examples/device/cherryusb_device_mtp/main/device_mtp_main.c b/examples/device/cherryusb_device_mtp/main/device_mtp_main.c new file mode 100644 index 0000000..3365922 --- /dev/null +++ b/examples/device/cherryusb_device_mtp/main/device_mtp_main.c @@ -0,0 +1,206 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" + +#include "usbd_core.h" +#include "usb_mtp.h" + +static char *TAG = "device_cdc_main"; + +#define WCID_VENDOR_CODE 0x01 + +__ALIGN_BEGIN const uint8_t WCID_StringDescriptor_MSOS[18] __ALIGN_END = { + /////////////////////////////////////// + /// MS OS string descriptor + /////////////////////////////////////// + 0x12, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + /* MSFT100 */ + 'M', 0x00, 'S', 0x00, 'F', 0x00, 'T', 0x00, /* wcChar_7 */ + '1', 0x00, '0', 0x00, '0', 0x00, /* wcChar_7 */ + WCID_VENDOR_CODE, /* bVendorCode */ + 0x00, /* bReserved */ +}; + +__ALIGN_BEGIN const uint8_t WINUSB_WCIDDescriptor[40] __ALIGN_END = { + /////////////////////////////////////// + /// WCID descriptor + /////////////////////////////////////// + 0x28, 0x00, 0x00, 0x00, /* dwLength */ + 0x00, 0x01, /* bcdVersion */ + 0x04, 0x00, /* wIndex */ + 0x01, /* bCount */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bReserved_7 */ + + /////////////////////////////////////// + /// WCID function descriptor + /////////////////////////////////////// + 0x00, /* bFirstInterfaceNumber */ + 0x01, /* bReserved */ + /* MTP */ + 'M', 'T', 'P', 0x00, 0x00, 0x00, 0x00, 0x00, /* cCID_8 */ + /* */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* cSubCID_8 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bReserved_6 */ +}; + +struct usb_msosv1_descriptor msosv1_desc = { + .string = (uint8_t *)WCID_StringDescriptor_MSOS, + .vendor_code = WCID_VENDOR_CODE, + .compat_id = (uint8_t *)WINUSB_WCIDDescriptor, + .comp_id_property = NULL, +}; + +/*!< endpoint address */ +#define CDC_IN_EP 0x81 +#define CDC_OUT_EP 0x02 +#define CDC_INT_EP 0x83 + +#define USBD_VID 0xFFFE +#define USBD_PID 0xFFFF +#define USBD_MAX_POWER 100 +#define USBD_LANGID_STRING 1033 + +/*!< config descriptor size */ +#define USB_CONFIG_SIZE (9 + MTP_DESCRIPTOR_LEN) + +#ifdef CONFIG_USB_HS +#define MTP_MAX_MPS 512 +#else +#define MTP_MAX_MPS 64 +#endif + +const uint8_t mtp_descriptor[] = { + USB_DEVICE_DESCRIPTOR_INIT(USB_2_1, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0201, 0x01), + USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), + MTP_DESCRIPTOR_INIT(0x00, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP, MTP_MAX_MPS, 0x02), + /////////////////////////////////////// + /// string0 descriptor + /////////////////////////////////////// + USB_LANGID_INIT(USBD_LANGID_STRING), + /////////////////////////////////////// + /// string1 descriptor + /////////////////////////////////////// + 0x14, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'U', 0x00, /* wcChar6 */ + 'S', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + /////////////////////////////////////// + /// string2 descriptor + /////////////////////////////////////// + 0x26, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'U', 0x00, /* wcChar6 */ + 'S', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + ' ', 0x00, /* wcChar9 */ + 'M', 0x00, /* wcChar10 */ + 'T', 0x00, /* wcChar11 */ + 'P', 0x00, /* wcChar12 */ + ' ', 0x00, /* wcChar13 */ + 'D', 0x00, /* wcChar14 */ + 'E', 0x00, /* wcChar15 */ + 'M', 0x00, /* wcChar16 */ + 'O', 0x00, /* wcChar17 */ + /////////////////////////////////////// + /// string3 descriptor + /////////////////////////////////////// + 0x16, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + '2', 0x00, /* wcChar0 */ + '0', 0x00, /* wcChar1 */ + '2', 0x00, /* wcChar2 */ + '1', 0x00, /* wcChar3 */ + '0', 0x00, /* wcChar4 */ + '3', 0x00, /* wcChar5 */ + '1', 0x00, /* wcChar6 */ + '0', 0x00, /* wcChar7 */ + '0', 0x00, /* wcChar8 */ + '0', 0x00, /* wcChar9 */ +#ifdef CONFIG_USB_HS + /////////////////////////////////////// + /// device qualifier descriptor + /////////////////////////////////////// + 0x0a, + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, + 0x00, + 0x02, + 0x02, + 0x02, + 0x01, + 0x40, + 0x01, + 0x00, +#endif + 0x00 +}; + +const uint8_t bos_descriptor[] = { + 0x05, 0x0f, 0x16, 0x00, 0x02, + 0x07, 0x10, 0x02, 0x06, 0x00, 0x00, 0x00, + 0x0a, 0x10, 0x03, 0x00, 0x0f, 0x00, 0x01, 0x01, 0xf4, 0x01 +}; + +void usbd_event_handler(uint8_t event) +{ + switch (event) { + case USBD_EVENT_RESET: + break; + case USBD_EVENT_CONNECTED: + break; + case USBD_EVENT_DISCONNECTED: + break; + case USBD_EVENT_RESUME: + break; + case USBD_EVENT_SUSPEND: + break; + case USBD_EVENT_CONFIGURED: + break; + case USBD_EVENT_SET_REMOTE_WAKEUP: + break; + case USBD_EVENT_CLR_REMOTE_WAKEUP: + break; + + default: + break; + } +} + +struct usbd_interface intf0; + +struct usb_bos_descriptor bos_desc = { + .string = bos_descriptor, + .string_len = sizeof(bos_descriptor) +}; + +void app_main(void) +{ + void sd_main(void); + sd_main(); + usbd_desc_register(mtp_descriptor); + usbd_msosv1_desc_register(&msosv1_desc); + usbd_bos_desc_register(&bos_desc); + usbd_add_interface(usbd_mtp_init_intf(&intf0, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP)); + usbd_initialize(); +} \ No newline at end of file diff --git a/examples/device/cherryusb_device_mtp/main/idf_component.yml b/examples/device/cherryusb_device_mtp/main/idf_component.yml new file mode 100644 index 0000000..c363614 --- /dev/null +++ b/examples/device/cherryusb_device_mtp/main/idf_component.yml @@ -0,0 +1,8 @@ +targets: + - esp32s2 + - esp32s3 +dependencies: + idf: ">=4.4.1" + udoudou/esp_cherryusb: + version: "0.0.*" + override_path: "../../../.." diff --git a/examples/device/cherryusb_device_mtp/main/sd_card_example_main.c b/examples/device/cherryusb_device_mtp/main/sd_card_example_main.c new file mode 100644 index 0000000..f55b2f2 --- /dev/null +++ b/examples/device/cherryusb_device_mtp/main/sd_card_example_main.c @@ -0,0 +1,97 @@ +/* SD card and FAT 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. +*/ + +// This example uses SDMMC peripheral to communicate with SD card. + +#include +#include +#include +#include "esp_vfs_fat.h" +#include "sdmmc_cmd.h" +#include "driver/sdmmc_host.h" + +static const char *TAG = "example"; + +#define MOUNT_POINT "/sdcard" + + +void sd_main(void) +{ + esp_err_t ret; + + // Options for mounting the filesystem. + // If format_if_mount_failed is set to true, SD card will be partitioned and + // formatted in case when mounting fails. + esp_vfs_fat_sdmmc_mount_config_t mount_config = { +#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED + .max_files = 5, + .allocation_unit_size = 16 * 1024 + }; + sdmmc_card_t *card; + const char mount_point[] = MOUNT_POINT; + ESP_LOGI(TAG, "Initializing SD card"); + + // Use settings defined above to initialize SD card and mount FAT filesystem. + // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions. + // Please check its source code and implement error recovery when developing + // production applications. + + ESP_LOGI(TAG, "Using SDMMC peripheral"); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + + // This initializes the slot without card detect (CD) and write protect (WP) signals. + // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + // Set bus width to use: +#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 + slot_config.width = 4; +#else + slot_config.width = 1; +#endif + + // On chips where the GPIOs used for SD card can be configured, set them in + // the slot_config structure: +#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX + slot_config.clk = CONFIG_EXAMPLE_PIN_CLK; + slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD; + slot_config.d0 = CONFIG_EXAMPLE_PIN_D0; +#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 + slot_config.d1 = CONFIG_EXAMPLE_PIN_D1; + slot_config.d2 = CONFIG_EXAMPLE_PIN_D2; + slot_config.d3 = CONFIG_EXAMPLE_PIN_D3; +#endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 +#endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX + + // Enable internal pullups on enabled pins. The internal pullups + // are insufficient however, please make sure 10k external pullups are + // connected on the bus. This is for debug / example purpose only. + slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; + + ESP_LOGI(TAG, "Mounting filesystem"); + ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card); + + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + ESP_LOGE(TAG, "Failed to mount filesystem. " + "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option."); + } else { + ESP_LOGE(TAG, "Failed to initialize the card (%s). " + "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret)); + } + return; + } + ESP_LOGI(TAG, "Filesystem mounted"); + + // Card has been initialized, print its properties + sdmmc_card_print_info(stdout, card); +} diff --git a/examples/device/cherryusb_device_mtp/sdkconfig.defaults b/examples/device/cherryusb_device_mtp/sdkconfig.defaults new file mode 100644 index 0000000..5d507af --- /dev/null +++ b/examples/device/cherryusb_device_mtp/sdkconfig.defaults @@ -0,0 +1,3 @@ +# ESP CherryUSB +CONFIG_CHERRYUSBD_ENABLED=y +CONFIG_FREERTOS_HZ=1000 From 31a11d4e6de5b798df8a70bb5fdd6a09eba18025 Mon Sep 17 00:00:00 2001 From: udoudou <949797067@qq.com> Date: Sat, 27 Jan 2024 17:24:42 +0800 Subject: [PATCH 2/6] Added support for time modification of new files --- .../components/esp_mtp/esp_mtp.c | 57 +++++++------ .../components/esp_mtp/esp_mtp_helper.c | 80 +++++++++++++++---- .../esp_mtp/include/esp_mtp_helper.h | 25 +++++- 3 files changed, 120 insertions(+), 42 deletions(-) diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c index 37c219f..9a988cc 100644 --- a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c @@ -13,6 +13,7 @@ #include "sys/stat.h" #include "unistd.h" #include "fcntl.h" +#include "utime.h" #include "esp_log.h" @@ -104,7 +105,7 @@ static mtp_response_code_t get_device_info(esp_mtp_handle_t handle) *(uint16_t *)data = MTP_VENDOR_EXTN_VERSION; // MTP Version data += 2; *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16(MTP_VENDOR_EXTENSIONDESC_CHAR, data + 1, data); // MTP Extensions + data = (uint8_t *)esp_mtp_utf8_to_utf16(MTP_VENDOR_EXTENSIONDESC_CHAR, (char *)data + 1, data); // MTP Extensions *(mtp_functional_mode_t *)data = MTP_FUNCTIONAL_STANDARD; // Functional Mode data += sizeof(mtp_functional_mode_t); @@ -143,16 +144,16 @@ static mtp_response_code_t get_device_info(esp_mtp_handle_t handle) data += sizeof(mtp_object_format_code_t); *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16("Espressif", data + 1, data); // Manufacturer + data = (uint8_t *)esp_mtp_utf8_to_utf16("Espressif", (char *)data + 1, data); // Manufacturer *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16("ESP32-S3", data + 1, data); // Model + data = (uint8_t *)esp_mtp_utf8_to_utf16("ESP32-S3", (char *)data + 1, data); // Model *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16("0.0.1", data + 1, data); // Device Version + data = (uint8_t *)esp_mtp_utf8_to_utf16("0.0.1", (char *)data + 1, data); // Device Version *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16("123456", data + 1, data); // Serial Number + data = (uint8_t *)esp_mtp_utf8_to_utf16("123456", (char *)data + 1, data); // Serial Number container->len = data - handle->buff; handle->write(handle->pipe_context, handle->buff, container->len); @@ -217,10 +218,10 @@ static mtp_response_code_t get_storage_info(esp_mtp_handle_t handle) *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16("test", data + 1, data); // Storage Description + data = (uint8_t *)esp_mtp_utf8_to_utf16("test", (char *)data + 1, data); // Storage Description *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16("0", data + 1, data); // Volume Identifier + data = (uint8_t *)esp_mtp_utf8_to_utf16("0", (char *)data + 1, data); // Volume Identifier container->len = data - handle->buff; handle->write(handle->pipe_context, handle->buff, container->len); @@ -376,26 +377,20 @@ static mtp_response_code_t get_object_info(esp_mtp_handle_t handle) data += sizeof(uint32_t); *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16(entry->name, data + 1, data); // Filename + data = (uint8_t *)esp_mtp_utf8_to_utf16(entry->name, (char *)data + 1, data); // Filename struct tm timeinfo; char timestr[48]; // Date Created "YYYYMMDDThhmmss.s" - time_t t = st.st_ctime; - localtime_r(&t, &timeinfo); - snprintf(timestr, sizeof(timestr), "%.4d%.2d%.2dT%.2d%.2d%.2d", 1900 + timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16(timestr, data + 1, data); + data = (uint8_t *)esp_mtp_time_to_utf16_datatime(st.st_ctime, (char *)data + 1, data); // Date Modified "YYYYMMDDThhmmss.s" - t = st.st_mtime; - localtime_r(&t, &timeinfo); - snprintf(timestr, sizeof(timestr), "%.4d%.2d%.2dT%.2d%.2d%.2d", 1900 + timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); *data = handle->buff + handle->buffer_size - data; - data = esp_mtp_utf8_to_utf16(timestr, data + 1, data); + data = (uint8_t *)esp_mtp_time_to_utf16_datatime(st.st_mtime, (char *)data + 1, data); // *data = handle->buff + handle->buffer_size - data; - // data = esp_mtp_utf8_to_utf16("", data + 1, data); // Keywords(未使用) + // data = (uint8_t*)esp_mtp_utf8_to_utf16("", (char*)data + 1, data); // Keywords(未使用) *data = 0x0; data++; @@ -593,6 +588,7 @@ static mtp_response_code_t send_object_info(esp_mtp_handle_t handle) { uint8_t *data; uint32_t parent_handle; + uint32_t object_handle; uint32_t storage_id; mtp_container_t *container = (mtp_container_t *)handle->buff; @@ -665,21 +661,24 @@ static mtp_response_code_t send_object_info(esp_mtp_handle_t handle) uint8_t str_len; str_len = *data; data += sizeof(uint8_t); - char filename[256]; - uint8_t temp_len; - esp_mtp_utf16_to_utf8((char *)data, (uint8_t *)filename, &temp_len); + char filename[255]; + uint8_t temp_len = sizeof(filename); + esp_mtp_utf16_to_utf8((char *)data, filename, &temp_len); ESP_LOGW(TAG, "%s %s", __FUNCTION__, filename); data += (str_len * 2); - //Ignore Date Created "YYYYMMDDThhmmss.s" + struct utimbuf times; + //Date Created "YYYYMMDDThhmmss.s" str_len = *data; data += sizeof(uint8_t); + times.actime = esp_mtp_utf16_datatime_to_time((char *)data); data += (str_len * 2); - //Ignore Date Modified "YYYYMMDDThhmmss.s" + //Date Modified "YYYYMMDDThhmmss.s" str_len = *data; data += sizeof(uint8_t); + times.modtime = esp_mtp_utf16_datatime_to_time((char *)data); data += (str_len * 2); //Skip No Use Keywords @@ -732,12 +731,13 @@ static mtp_response_code_t send_object_info(esp_mtp_handle_t handle) if (container->response.send_object_info.parent_handle == 0) { container->response.send_object_info.parent_handle = 0xFFFFFFFF; } - container->response.send_object_info.object_handle = esp_mtp_file_list_add(&handle->handle_list, 0x00010001, parent_handle, filename); - if (container->response.send_object_info.object_handle == 0) { + object_handle = esp_mtp_file_list_add(&handle->handle_list, 0x00010001, parent_handle, filename); + if (object_handle == 0) { ESP_LOGW(TAG, "add file list fail"); req = MTP_RESPONSE_ACCESS_DENIED; goto exit; } + container->response.send_object_info.object_handle = object_handle; container->len = MTP_CONTAINER_HEAD_LEN + 12; container->type = MTP_CONTAINER_RESPONSE; container->res = MTP_RESPONSE_OK; @@ -826,10 +826,19 @@ static mtp_response_code_t send_object_info(esp_mtp_handle_t handle) ESP_LOGI(TAG, "recv file ok"); } } + exit: if (fd >= 0) { close(fd); } + if (object_handle != 0) { + //todo + strcpy((char *)container->data, "/sdcard"); + data = container->data + strlen((char *)container->data); + if (esp_mtp_file_list_find(&handle->handle_list, object_handle, (char *)data, handle->buff + handle->buffer_size - data) != NULL) { + utime((char *)container->data, ×); + } + } return req; } diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c index acada71..c246a19 100644 --- a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c @@ -10,11 +10,11 @@ #include #include "inttypes.h" -uint8_t *esp_mtp_utf8_to_utf16(const char *utf8, uint8_t *out, uint8_t *len) +char *esp_mtp_utf8_to_utf16(const char *utf8, char *out, uint8_t *len) { - uint16_t *out_start = (uint16_t *)out; - uint16_t *out_end = out_start; - while (*utf8 != '\0') { + uint16_t *utf16_ptr = (uint16_t *)out; + uint16_t *utf16_end_ptr = utf16_ptr + (*len / 2) - 1; + while (*utf8 != '\0' && utf16_ptr < utf16_end_ptr) { uint16_t temp; temp = *(utf8++); if (temp & 0x80) { @@ -42,36 +42,84 @@ uint8_t *esp_mtp_utf8_to_utf16(const char *utf8, uint8_t *out, uint8_t *len) break; } } - *(out_end++) = temp; + *(utf16_ptr++) = temp; } - *(out_end++) = 0x0000; - *len = out_end - out_start; - return (uint8_t *)out_end; + *(utf16_ptr++) = 0x0000; + *len = utf16_ptr - (uint16_t *)out; + return (char *)utf16_ptr; } -uint8_t *esp_mtp_utf16_to_utf8(const char *utf16, uint8_t *out, uint8_t *len) +char *esp_mtp_time_to_utf16_datatime(time_t time, char *out, uint8_t *len) +{ + struct tm timeinfo; + char buff[24]; + localtime_r(&time, &timeinfo); + // snprintf(buff, sizeof(buff), "%.4d%.2d%.2dT%.2d%.2d%.2d", 1900 + timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); + strftime(buff, sizeof(buff), "%Y%m%dT%H%M%S", &timeinfo); + return esp_mtp_utf8_to_utf16(buff, out, len); +} + +char *esp_mtp_utf16_to_utf8(const char *utf16, char *out, uint8_t *len) { uint16_t *utf16_ptr = (uint16_t *)utf16; - uint8_t *out_start = out; + char *out_end = out + *len - 1; + *len = 1; while (*utf16_ptr != 0x0) { uint16_t temp = *(utf16_ptr++); if (temp <= 0x7F) { + if (out >= out_end) { + break; + } *(out++) = temp; - continue; } else if (temp <= 0x7FF) { + if (out + 1 >= out_end) { + break; + } *(out++) = (temp >> 6) | 0xC0; *(out++) = (temp & 0x3F) | 0x80; - continue; + } else { + if (out + 2 >= out_end) { + break; + } + *(out++) = (temp >> 12) | 0xE0; + *(out++) = ((temp >> 6) & 0x3F) | 0x80; + *(out++) = (temp & 0x3F) | 0x80; } - *(out++) = (temp >> 12) | 0xE0; - *(out++) = ((temp >> 6) & 0x3F) | 0x80; - *(out++) = (temp & 0x3F) | 0x80; + *len = *len + 1; } *(out++) = 0x00; - *len = (out - out_start); return out; } +time_t esp_mtp_utf16_datatime_to_time(const char *utf16) +{ + //"YYYYMMDDThhmmss.s",".s" 可选 + char buff[24]; + uint16_t *utf16_ptr = (uint16_t *)utf16; + uint16_t *time_end = utf16_ptr + (sizeof("YYYYMMDDThhmmss") - 1); + if (*time_end != 0 && *time_end != '.') { + return 0; + } + char *data = buff; + while (utf16_ptr < time_end) { + if (*utf16_ptr > 0x7F) { + return 0; + } + *(data++) = *(utf16_ptr++); + uint32_t off; + off = utf16_ptr - (uint16_t *)utf16; + if (off == 4 || off == 6 || off == 11 || off == 13) { + *(data++) = '-'; + } + } + *(data) = '\0'; + struct tm tm_time; + if (strptime((char *)buff, "%Y-%m-%dT%H-%M-%S", &tm_time) != data) { + return 0; + } + return mktime(&tm_time); +} + void esp_mtp_file_list_init(esp_mtp_file_handle_list_t *file_list) { memset(file_list, 0, sizeof(esp_mtp_file_handle_list_t)); diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_helper.h b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_helper.h index 29593ce..ad0a6ae 100644 --- a/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_helper.h +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp_helper.h @@ -6,6 +6,7 @@ #pragma once +#include #include #define MTP_FILE_LIST_SIZE 64 @@ -26,9 +27,29 @@ typedef struct { esp_mtp_file_list_t list; }esp_mtp_file_handle_list_t; -uint8_t *esp_mtp_utf8_to_utf16(const char *utf8, uint8_t *out, uint8_t *len); +/** @brief UTF8 转 UTF16 + * + * @param utf16 需要转换的 UTF8 字符串 + * @param[out] out 保存转换后的 UTF16 字符串 + * @param[in out] len 输入保存空间的长度,输出完成转换的 UTF16 字符数(含结束符) + * + * @return 保存结束符的下一个内存地址 + */ +char *esp_mtp_utf8_to_utf16(const char *utf8, char *out, uint8_t *len); + +char *esp_mtp_time_to_utf16_datatime(time_t time, char *out, uint8_t *len); + +/** @brief UTF16 转 UTF8 + * + * @param utf16 需要转换的 UTF16 字符串 + * @param[out] out 保存转换后的 UTF8 字符串 + * @param[in out] len 输入保存空间的长度,输出完成转换的 UTF8 字符数(含结束符) + * + * @return 保存结束符的下一个内存地址 + */ +char *esp_mtp_utf16_to_utf8(const char *utf16, char *out, uint8_t *len); -uint8_t *esp_mtp_utf16_to_utf8(const char *utf16, uint8_t *out, uint8_t *len); +time_t esp_mtp_utf16_datatime_to_time(const char *utf16); void esp_mtp_file_list_init(esp_mtp_file_handle_list_t *file_list); From 1085d52c384fcdb7eca0bb43df76000562c797d0 Mon Sep 17 00:00:00 2001 From: udoudou <949797067@qq.com> Date: Sat, 27 Jan 2024 17:43:51 +0800 Subject: [PATCH 3/6] Add support for obtaining capacity space --- .../components/esp_mtp/CMakeLists.txt | 2 +- .../cherryusb_device_mtp/components/esp_mtp/esp_mtp.c | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/CMakeLists.txt b/examples/device/cherryusb_device_mtp/components/esp_mtp/CMakeLists.txt index d0be680..0a36aa3 100644 --- a/examples/device/cherryusb_device_mtp/components/esp_mtp/CMakeLists.txt +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register(SRCS "esp_mtp.c" "esp_mtp_helper.c" INCLUDE_DIRS "include" - PRIV_REQUIRES + PRIV_REQUIRES fatfs ) diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c index 9a988cc..fe15e3c 100644 --- a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c @@ -15,6 +15,7 @@ #include "fcntl.h" #include "utime.h" +#include "esp_vfs_fat.h" #include "esp_log.h" static char *TAG = "esp_mtp"; @@ -206,11 +207,12 @@ static mtp_response_code_t get_storage_info(esp_mtp_handle_t handle) *(mtp_access_cap_t *)data = MTP_ACCESS_CAP_RW; // Access Capability data += sizeof(mtp_access_cap_t); - - *(uint64_t *)data = 1024 * 1024 * 1024; // Max Capacity + uint64_t total_bytes = 0, out_free_bytes = 0; + esp_vfs_fat_info("/sdcard", &total_bytes, &out_free_bytes); + *(uint64_t *)data = total_bytes; // Max Capacity data += sizeof(uint64_t); - *(uint64_t *)data = 1024; // Free space in Bytes + *(uint64_t *)data = out_free_bytes; // Free space in Bytes data += sizeof(uint64_t); *(uint32_t *)data = 0xFFFFFFFF; // Free Space In Objects @@ -379,8 +381,6 @@ static mtp_response_code_t get_object_info(esp_mtp_handle_t handle) *data = handle->buff + handle->buffer_size - data; data = (uint8_t *)esp_mtp_utf8_to_utf16(entry->name, (char *)data + 1, data); // Filename - struct tm timeinfo; - char timestr[48]; // Date Created "YYYYMMDDThhmmss.s" *data = handle->buff + handle->buffer_size - data; data = (uint8_t *)esp_mtp_time_to_utf16_datatime(st.st_ctime, (char *)data + 1, data); @@ -1077,7 +1077,6 @@ void esp_mtp_read_async_cb(esp_mtp_handle_t handle, int len) void esp_mtp_write_async_cb(esp_mtp_handle_t handle, int len) { - // USB_LOG_DBG("send:%d\r\n", len); BaseType_t high_task_wakeup = pdFALSE; xTaskNotifyFromISR(handle->task_hdl, ASYNC_WRITE_NOTIFY_BIT, eSetBits, &high_task_wakeup); if (high_task_wakeup == pdTRUE) { From 1edfad43cbe5c25d92270f7702affe0249c0b728 Mon Sep 17 00:00:00 2001 From: udoudou <949797067@qq.com> Date: Tue, 30 Jan 2024 00:39:36 +0800 Subject: [PATCH 4/6] Add deinit interface --- .../components/esp_mtp/esp_mtp.c | 49 +++++++++----- .../components/esp_mtp/esp_mtp_helper.c | 34 ++++++++++ .../components/esp_mtp/include/esp_mtp.h | 4 ++ .../components/usb_mtp/include/usb_mtp.h | 4 +- .../components/usb_mtp/usb_mtp.c | 66 ++++++++++++++++++- .../main/device_mtp_main.c | 15 +++++ 6 files changed, 153 insertions(+), 19 deletions(-) diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c index fe15e3c..5f02455 100644 --- a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp.c @@ -17,6 +17,9 @@ #include "esp_vfs_fat.h" #include "esp_log.h" +#ifdef CONFIG_SPIRAM_BOOT_INIT +#include "esp_heap_caps.h" +#endif static char *TAG = "esp_mtp"; @@ -25,6 +28,7 @@ static char *TAG = "esp_mtp"; typedef struct esp_mtp { void *pipe_context; + void (*wait_start)(void *pipe_context); int (*read)(void *pipe_context, uint8_t *buffer, int len); int (*write)(void *pipe_context, const uint8_t *buffer, int len); esp_mtp_flags_t flags; @@ -315,7 +319,7 @@ static mtp_response_code_t get_object_info(esp_mtp_handle_t handle) if (entry == NULL) { return MTP_RESPONSE_INVALID_OBJECT_HANDLE; } - ESP_LOGW(TAG, "%s %s", __FUNCTION__, (char *)container->data); + ESP_LOGD(TAG, "%s %s", __FUNCTION__, (char *)container->data); struct stat st; if (stat((char *)container->data, &st) != 0) { return MTP_RESPONSE_INVALID_OBJECT_HANDLE; @@ -414,7 +418,7 @@ static mtp_response_code_t _get_object_common(esp_mtp_handle_t handle, uint32_t if (esp_mtp_file_list_find(&handle->handle_list, object_handle, (char *)data, handle->buff + handle->buffer_size - data) == NULL) { return MTP_RESPONSE_INVALID_OBJECT_HANDLE; } - ESP_LOGW(TAG, "%s %s", __FUNCTION__, (char *)container->data); + ESP_LOGD(TAG, "%s %s", __FUNCTION__, (char *)container->data); int fd; struct stat st; @@ -664,7 +668,7 @@ static mtp_response_code_t send_object_info(esp_mtp_handle_t handle) char filename[255]; uint8_t temp_len = sizeof(filename); esp_mtp_utf16_to_utf8((char *)data, filename, &temp_len); - ESP_LOGW(TAG, "%s %s", __FUNCTION__, filename); + ESP_LOGD(TAG, "%s %s", __FUNCTION__, filename); data += (str_len * 2); @@ -709,7 +713,7 @@ static mtp_response_code_t send_object_info(esp_mtp_handle_t handle) *data = '/'; data++; strcpy((char *)data, filename); - ESP_LOGW(TAG, "%s %s", __FUNCTION__, (char *)container->data); + ESP_LOGD(TAG, "%s %s", __FUNCTION__, (char *)container->data); int fd = -1; if (object_format != MTP_OBJECT_FORMAT_ASSOCIATION) { @@ -958,7 +962,9 @@ static void esp_mtp_task(void *args) container = (mtp_container_t *)handle->buff; wait: - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + esp_mtp_file_list_clean(&handle->handle_list); + handle->wait_start(handle->pipe_context); + ESP_LOGW(TAG, "MTP task start"); while (1) { len = handle->read(handle->pipe_context, handle->buff, handle->buffer_size); if (handle->flags & ESP_MTP_FLAG_ASYNC_READ) { @@ -1032,6 +1038,7 @@ static void esp_mtp_task(void *args) } ESP_LOGW(TAG, "MTP task exit"); + esp_mtp_file_list_clean(&handle->handle_list); free(handle); vTaskDelete(NULL); } @@ -1044,12 +1051,16 @@ esp_mtp_handle_t esp_mtp_init(const esp_mtp_config_t *config) if (buffer_size < 1024) { buffer_size = 1024; } - +#ifndef CONFIG_SPIRAM_USE_MALLOC handle = malloc(sizeof(esp_mtp_t) + MTP_CONTAINER_HEAD_LEN + buffer_size); +#else + handle = heap_caps_malloc(sizeof(esp_mtp_t) + MTP_CONTAINER_HEAD_LEN + buffer_size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL); +#endif esp_mtp_file_list_init(&handle->handle_list); handle->pipe_context = config->pipe_context; + handle->wait_start = config->wait_start; handle->read = config->read; handle->write = config->write; handle->flags = config->flags; @@ -1066,21 +1077,29 @@ esp_mtp_handle_t esp_mtp_init(const esp_mtp_config_t *config) void esp_mtp_read_async_cb(esp_mtp_handle_t handle, int len) { - BaseType_t high_task_wakeup; - high_task_wakeup = pdFALSE; handle->async_read_len = len; - xTaskNotifyFromISR(handle->task_hdl, ASYNC_READ_NOTIFY_BIT, eSetBits, &high_task_wakeup); - if (high_task_wakeup == pdTRUE) { - portYIELD_FROM_ISR(); + if (xPortInIsrContext()) { + BaseType_t high_task_wakeup; + high_task_wakeup = pdFALSE; + xTaskNotifyFromISR(handle->task_hdl, ASYNC_READ_NOTIFY_BIT, eSetBits, &high_task_wakeup); + if (high_task_wakeup == pdTRUE) { + portYIELD_FROM_ISR(); + } + } else { + xTaskNotify(handle->task_hdl, ASYNC_READ_NOTIFY_BIT, eSetBits); } } void esp_mtp_write_async_cb(esp_mtp_handle_t handle, int len) { - BaseType_t high_task_wakeup = pdFALSE; - xTaskNotifyFromISR(handle->task_hdl, ASYNC_WRITE_NOTIFY_BIT, eSetBits, &high_task_wakeup); - if (high_task_wakeup == pdTRUE) { - portYIELD_FROM_ISR(); + if (xPortInIsrContext()) { + BaseType_t high_task_wakeup = pdFALSE; + xTaskNotifyFromISR(handle->task_hdl, ASYNC_WRITE_NOTIFY_BIT, eSetBits, &high_task_wakeup); + if (high_task_wakeup == pdTRUE) { + portYIELD_FROM_ISR(); + } + } else { + xTaskNotify(handle->task_hdl, ASYNC_WRITE_NOTIFY_BIT, eSetBits); } } diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c index c246a19..bc98b09 100644 --- a/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/esp_mtp_helper.c @@ -10,6 +10,10 @@ #include #include "inttypes.h" +#ifdef CONFIG_SPIRAM_BOOT_INIT +#include "esp_heap_caps.h" +#endif + char *esp_mtp_utf8_to_utf16(const char *utf8, char *out, uint8_t *len) { uint16_t *utf16_ptr = (uint16_t *)out; @@ -131,14 +135,22 @@ uint32_t esp_mtp_file_list_add(esp_mtp_file_handle_list_t *file_list, uint32_t s list = &file_list->list; for (uint32_t i = 1; i <= file_list->count / MTP_FILE_LIST_SIZE; i++) { if (list->next == NULL) { +#if defined CONFIG_SPIRAM_USE_MALLOC || defined CONFIG_SPIRAM_USE_CAPS_ALLOC + list->next = (esp_mtp_file_list_t *)heap_caps_calloc(1, sizeof(esp_mtp_file_list_t), MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM); +#else list->next = (esp_mtp_file_list_t *)calloc(1, sizeof(esp_mtp_file_list_t)); +#endif if (list->next == NULL) { return 0; } } list = list->next; } +#if defined CONFIG_SPIRAM_USE_MALLOC || defined CONFIG_SPIRAM_USE_CAPS_ALLOC + list->entry_list[file_list->count % MTP_FILE_LIST_SIZE].name = (char *)heap_caps_malloc(strlen(name) + 1, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM); +#else list->entry_list[file_list->count % MTP_FILE_LIST_SIZE].name = (char *)malloc(strlen(name) + 1); +#endif if (list->entry_list[file_list->count % MTP_FILE_LIST_SIZE].name == NULL) { return 0; } @@ -184,6 +196,28 @@ const esp_mtp_file_entry_t *esp_mtp_file_list_find(esp_mtp_file_handle_list_t *f return entry; } +void esp_mtp_file_list_clean(esp_mtp_file_handle_list_t *file_list) +{ + esp_mtp_file_list_t *list; + esp_mtp_file_list_t *next; + + list = &file_list->list; + do { + for (uint32_t i = 0; i < MTP_FILE_LIST_SIZE; i++) { + if (list->entry_list[i].name) { + free(list->entry_list[i].name); + } + } + next = list->next; + if (list != &file_list->list) { + free(list); + } + list = next; + } while (list != NULL); + memset(&file_list->list, 0, sizeof(file_list->list)); + file_list->count = 0; +} + uint8_t *esp_mtp_file_list_fill_handle_array(esp_mtp_file_handle_list_t *file_list, uint32_t parent, uint8_t *out, uint32_t *len) { uint32_t max_count; diff --git a/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp.h b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp.h index 5da4864..ddeec05 100644 --- a/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp.h +++ b/examples/device/cherryusb_device_mtp/components/esp_mtp/include/esp_mtp.h @@ -22,8 +22,12 @@ typedef enum { ESP_MTP_FLAG_MAX = 0xFFFFFFFF, } __attribute__((packed)) esp_mtp_flags_t; +#define ESP_MTP_STOP_CMD 0 +#define ESP_MTP_EXIT_CMD -1 + typedef struct { void *pipe_context; + void (*wait_start)(void *pipe_context); int (*read)(void *pipe_context, uint8_t *buffer, int len); int (*write)(void *pipe_context, const uint8_t *buffer, int len); esp_mtp_flags_t flags; diff --git a/examples/device/cherryusb_device_mtp/components/usb_mtp/include/usb_mtp.h b/examples/device/cherryusb_device_mtp/components/usb_mtp/include/usb_mtp.h index 6551ddc..0b19b02 100644 --- a/examples/device/cherryusb_device_mtp/components/usb_mtp/include/usb_mtp.h +++ b/examples/device/cherryusb_device_mtp/components/usb_mtp/include/usb_mtp.h @@ -52,4 +52,6 @@ struct usbd_interface *usbd_mtp_init_intf(struct usbd_interface *intf, const uint8_t out_ep, const uint8_t in_ep, - const uint8_t int_ep); \ No newline at end of file + const uint8_t int_ep); + +void usbd_mtp_deinit(void); \ No newline at end of file diff --git a/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c b/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c index 6f9fa2e..fa3d388 100644 --- a/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c +++ b/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c @@ -19,10 +19,19 @@ #define MTP_IN_EP_IDX 1 #define MTP_INT_EP_IDX 2 +typedef enum { + USB_MTP_CLOSE, + USB_MTP_INIT, + USB_MTP_RUN, + USB_MTP_STOPPING, +} usb_mtp_status_t; + /* Describe EndPoints configuration */ static struct usbd_endpoint mtp_ep_data[3]; static TaskHandle_t s_mtp_task_handle; esp_mtp_handle_t s_handle; +static usb_mtp_status_t s_mtp_status = USB_MTP_CLOSE; +static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED; static int mtp_class_interface_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) { @@ -68,11 +77,23 @@ static void mtp_notify_handler(uint8_t event, void *arg) BaseType_t high_task_wakeup = pdFALSE; switch (event) { case USBD_EVENT_RESET: + portENTER_CRITICAL_ISR(&s_spinlock); + if (s_mtp_status != USB_MTP_CLOSE && s_mtp_status != USB_MTP_INIT) { + s_mtp_status = USB_MTP_STOPPING; + } + portEXIT_CRITICAL_ISR(&s_spinlock); break; case USBD_EVENT_CONFIGURED: - USB_LOG_DBG("Start reading command\r\n"); - vTaskNotifyGiveFromISR(s_mtp_task_handle, &high_task_wakeup); - + bool need_wake = false; + portENTER_CRITICAL_ISR(&s_spinlock); + if (s_mtp_status == USB_MTP_INIT) { + need_wake = true; + s_mtp_status = USB_MTP_RUN; + } + portEXIT_CRITICAL_ISR(&s_spinlock); + if (need_wake) { + vTaskNotifyGiveFromISR(s_mtp_task_handle, &high_task_wakeup); + } break; case USBD_EVENT_DISCONNECTED: vTaskNotifyGiveFromISR(s_mtp_task_handle, &high_task_wakeup); @@ -85,14 +106,36 @@ static void mtp_notify_handler(uint8_t event, void *arg) } } +static void usb_wait_start(void *pipe_context) +{ + portENTER_CRITICAL(&s_spinlock); + if (s_mtp_status != USB_MTP_STOPPING) { + portEXIT_CRITICAL(&s_spinlock); + return; + } + s_mtp_status = USB_MTP_INIT; + portEXIT_CRITICAL(&s_spinlock); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); +} + static int usb_write(void *pipe_context, const uint8_t *data, int data_size) { + if (s_mtp_status != USB_MTP_RUN) { + data_size = (s_mtp_status != USB_MTP_CLOSE) ? ESP_MTP_STOP_CMD : ESP_MTP_EXIT_CMD; + esp_mtp_write_async_cb(s_handle, data_size); + return data_size; + } usbd_ep_start_write(mtp_ep_data[MTP_IN_EP_IDX].ep_addr, data, data_size); return data_size; } static int usb_read(void *pipe_context, uint8_t *data, int data_size) { + if (s_mtp_status != USB_MTP_RUN) { + data_size = (s_mtp_status != USB_MTP_CLOSE) ? ESP_MTP_STOP_CMD : ESP_MTP_EXIT_CMD; + esp_mtp_read_async_cb(s_handle, data_size); + return data_size; + } usbd_ep_start_read(mtp_ep_data[MTP_OUT_EP_IDX].ep_addr, data, data_size); return data_size; } @@ -120,7 +163,10 @@ struct usbd_interface *usbd_mtp_init_intf(struct usbd_interface *intf, usbd_add_endpoint(&mtp_ep_data[MTP_IN_EP_IDX]); usbd_add_endpoint(&mtp_ep_data[MTP_INT_EP_IDX]); + s_mtp_status = USB_MTP_STOPPING; + esp_mtp_config_t config = { + .wait_start = usb_wait_start, .read = usb_read, .write = usb_write, .flags = ESP_MTP_FLAG_ASYNC_READ | ESP_MTP_FLAG_ASYNC_WRITE, @@ -137,4 +183,18 @@ struct usbd_interface *usbd_mtp_init_intf(struct usbd_interface *intf, s_mtp_task_handle = esp_mtp_get_task_handle(s_handle); return intf; +} + +void usbd_mtp_deinit(void) +{ + usb_mtp_status_t mtp_status; + portENTER_CRITICAL(&s_spinlock); + mtp_status = s_mtp_status; + s_mtp_status = USB_MTP_CLOSE; + portEXIT_CRITICAL(&s_spinlock); + if (mtp_status == USB_MTP_RUN) { + esp_mtp_read_async_cb(s_handle, ESP_MTP_EXIT_CMD); + } else if (mtp_status == USB_MTP_INIT) { + xTaskNotifyGive(s_mtp_task_handle); + } } \ No newline at end of file diff --git a/examples/device/cherryusb_device_mtp/main/device_mtp_main.c b/examples/device/cherryusb_device_mtp/main/device_mtp_main.c index 3365922..d3a40fd 100644 --- a/examples/device/cherryusb_device_mtp/main/device_mtp_main.c +++ b/examples/device/cherryusb_device_mtp/main/device_mtp_main.c @@ -198,9 +198,24 @@ void app_main(void) { void sd_main(void); sd_main(); + uint32_t before = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); usbd_desc_register(mtp_descriptor); usbd_msosv1_desc_register(&msosv1_desc); usbd_bos_desc_register(&bos_desc); usbd_add_interface(usbd_mtp_init_intf(&intf0, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP)); usbd_initialize(); + while (1){ + vTaskDelay(10000 / portTICK_PERIOD_MS); + usbd_mtp_deinit(); + usbd_deinitialize(); + vTaskDelay(500 / portTICK_PERIOD_MS); + uint32_t now; + now = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); + ESP_LOGW(TAG, "use %"PRIu32, before - now); + usbd_desc_register(mtp_descriptor); + usbd_msosv1_desc_register(&msosv1_desc); + usbd_bos_desc_register(&bos_desc); + usbd_add_interface(usbd_mtp_init_intf(&intf0, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP)); + usbd_initialize(); + } } \ No newline at end of file From 006611598af846cc4ec81fe6064f1eac7a6e6925 Mon Sep 17 00:00:00 2001 From: udoudou <949797067@qq.com> Date: Tue, 7 May 2024 22:22:56 +0800 Subject: [PATCH 5/6] Fixed bug: routine descriptor configuration error caused crash --- Kconfig | 31 +++++++++ .../main/device_mtp_main.c | 63 +------------------ 2 files changed, 33 insertions(+), 61 deletions(-) diff --git a/Kconfig b/Kconfig index d7ad4d0..8eded0c 100644 --- a/Kconfig +++ b/Kconfig @@ -6,6 +6,37 @@ menu "ESP CherryUSB" bool default y if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + choice USB_DBG_LEVEL + bool "Default log verbosity" + default USB_DBG_LEVEL_INFO + help + Specify how much output to see in logs by default. + You can set lower verbosity level at runtime using + esp_log_level_set function. + + By default, this setting limits which log statements + are compiled into the program. For example, selecting + "Warning" would mean that changing log level to "Debug" + at runtime will not be possible. To allow increasing log + level above the default at runtime, see the next option. + + config USB_DBG_LEVEL_ERROR + bool "Error" + config USB_DBG_LEVEL_WARN + bool "Warning" + config USB_DBG_LEVEL_INFO + bool "Info" + config USB_DBG_LEVEL_DEBUG + bool "Debug" + endchoice + + config USB_DBG_LEVEL + int + default 0 if USB_DBG_LEVEL_ERROR + default 1 if USB_DBG_LEVEL_WARN + default 2 if USB_DBG_LEVEL_INFO + default 3 if USB_DBG_LEVEL_DEBUG + menuconfig CHERRYUSBD_ENABLED bool "Enable CherryUSB Device" depends on CHERRYUSB_SUPPORTED diff --git a/examples/device/cherryusb_device_mtp/main/device_mtp_main.c b/examples/device/cherryusb_device_mtp/main/device_mtp_main.c index d3a40fd..5e73e18 100644 --- a/examples/device/cherryusb_device_mtp/main/device_mtp_main.c +++ b/examples/device/cherryusb_device_mtp/main/device_mtp_main.c @@ -13,51 +13,7 @@ #include "usbd_core.h" #include "usb_mtp.h" -static char *TAG = "device_cdc_main"; - -#define WCID_VENDOR_CODE 0x01 - -__ALIGN_BEGIN const uint8_t WCID_StringDescriptor_MSOS[18] __ALIGN_END = { - /////////////////////////////////////// - /// MS OS string descriptor - /////////////////////////////////////// - 0x12, /* bLength */ - USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ - /* MSFT100 */ - 'M', 0x00, 'S', 0x00, 'F', 0x00, 'T', 0x00, /* wcChar_7 */ - '1', 0x00, '0', 0x00, '0', 0x00, /* wcChar_7 */ - WCID_VENDOR_CODE, /* bVendorCode */ - 0x00, /* bReserved */ -}; - -__ALIGN_BEGIN const uint8_t WINUSB_WCIDDescriptor[40] __ALIGN_END = { - /////////////////////////////////////// - /// WCID descriptor - /////////////////////////////////////// - 0x28, 0x00, 0x00, 0x00, /* dwLength */ - 0x00, 0x01, /* bcdVersion */ - 0x04, 0x00, /* wIndex */ - 0x01, /* bCount */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bReserved_7 */ - - /////////////////////////////////////// - /// WCID function descriptor - /////////////////////////////////////// - 0x00, /* bFirstInterfaceNumber */ - 0x01, /* bReserved */ - /* MTP */ - 'M', 'T', 'P', 0x00, 0x00, 0x00, 0x00, 0x00, /* cCID_8 */ - /* */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* cSubCID_8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bReserved_6 */ -}; - -struct usb_msosv1_descriptor msosv1_desc = { - .string = (uint8_t *)WCID_StringDescriptor_MSOS, - .vendor_code = WCID_VENDOR_CODE, - .compat_id = (uint8_t *)WINUSB_WCIDDescriptor, - .comp_id_property = NULL, -}; +static char *TAG = "device_mtp_main"; /*!< endpoint address */ #define CDC_IN_EP 0x81 @@ -79,7 +35,7 @@ struct usb_msosv1_descriptor msosv1_desc = { #endif const uint8_t mtp_descriptor[] = { - USB_DEVICE_DESCRIPTOR_INIT(USB_2_1, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0201, 0x01), + USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0201, 0x01), USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), MTP_DESCRIPTOR_INIT(0x00, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP, MTP_MAX_MPS, 0x02), /////////////////////////////////////// @@ -156,12 +112,6 @@ const uint8_t mtp_descriptor[] = { 0x00 }; -const uint8_t bos_descriptor[] = { - 0x05, 0x0f, 0x16, 0x00, 0x02, - 0x07, 0x10, 0x02, 0x06, 0x00, 0x00, 0x00, - 0x0a, 0x10, 0x03, 0x00, 0x0f, 0x00, 0x01, 0x01, 0xf4, 0x01 -}; - void usbd_event_handler(uint8_t event) { switch (event) { @@ -189,19 +139,12 @@ void usbd_event_handler(uint8_t event) struct usbd_interface intf0; -struct usb_bos_descriptor bos_desc = { - .string = bos_descriptor, - .string_len = sizeof(bos_descriptor) -}; - void app_main(void) { void sd_main(void); sd_main(); uint32_t before = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); usbd_desc_register(mtp_descriptor); - usbd_msosv1_desc_register(&msosv1_desc); - usbd_bos_desc_register(&bos_desc); usbd_add_interface(usbd_mtp_init_intf(&intf0, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP)); usbd_initialize(); while (1){ @@ -213,8 +156,6 @@ void app_main(void) now = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); ESP_LOGW(TAG, "use %"PRIu32, before - now); usbd_desc_register(mtp_descriptor); - usbd_msosv1_desc_register(&msosv1_desc); - usbd_bos_desc_register(&bos_desc); usbd_add_interface(usbd_mtp_init_intf(&intf0, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP)); usbd_initialize(); } From 4940a2e21cddaa5648a709da65c836c7f51c63ab Mon Sep 17 00:00:00 2001 From: udoudou <949797067@qq.com> Date: Mon, 10 Jun 2024 23:58:31 +0800 Subject: [PATCH 6/6] Adapt to v1.3.0 version --- .../components/usb_mtp/usb_mtp.c | 18 ++++++++--------- .../main/device_mtp_main.c | 20 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c b/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c index fa3d388..bb38297 100644 --- a/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c +++ b/examples/device/cherryusb_device_mtp/components/usb_mtp/usb_mtp.c @@ -33,7 +33,7 @@ esp_mtp_handle_t s_handle; static usb_mtp_status_t s_mtp_status = USB_MTP_CLOSE; static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED; -static int mtp_class_interface_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +static int mtp_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) { USB_LOG_DBG("MTP Class request: " "bRequest 0x%02x\r\n", @@ -62,17 +62,17 @@ static int mtp_class_interface_request_handler(struct usb_setup_packet *setup, u return 0; } -static void usbd_mtp_bulk_out(uint8_t ep, uint32_t nbytes) +static void usbd_mtp_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) { esp_mtp_read_async_cb(s_handle, nbytes); } -static void usbd_mtp_bulk_in(uint8_t ep, uint32_t nbytes) +static void usbd_mtp_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) { esp_mtp_write_async_cb(s_handle, nbytes); } -static void mtp_notify_handler(uint8_t event, void *arg) +static void mtp_notify_handler(uint8_t busid, uint8_t event, void *arg) { BaseType_t high_task_wakeup = pdFALSE; switch (event) { @@ -125,7 +125,7 @@ static int usb_write(void *pipe_context, const uint8_t *data, int data_size) esp_mtp_write_async_cb(s_handle, data_size); return data_size; } - usbd_ep_start_write(mtp_ep_data[MTP_IN_EP_IDX].ep_addr, data, data_size); + usbd_ep_start_write(0, mtp_ep_data[MTP_IN_EP_IDX].ep_addr, data, data_size); return data_size; } @@ -136,7 +136,7 @@ static int usb_read(void *pipe_context, uint8_t *data, int data_size) esp_mtp_read_async_cb(s_handle, data_size); return data_size; } - usbd_ep_start_read(mtp_ep_data[MTP_OUT_EP_IDX].ep_addr, data, data_size); + usbd_ep_start_read(0, mtp_ep_data[MTP_OUT_EP_IDX].ep_addr, data, data_size); return data_size; } @@ -159,9 +159,9 @@ struct usbd_interface *usbd_mtp_init_intf(struct usbd_interface *intf, mtp_ep_data[MTP_INT_EP_IDX].ep_addr = int_ep; mtp_ep_data[MTP_INT_EP_IDX].ep_cb = NULL; - usbd_add_endpoint(&mtp_ep_data[MTP_OUT_EP_IDX]); - usbd_add_endpoint(&mtp_ep_data[MTP_IN_EP_IDX]); - usbd_add_endpoint(&mtp_ep_data[MTP_INT_EP_IDX]); + usbd_add_endpoint(0, &mtp_ep_data[MTP_OUT_EP_IDX]); + usbd_add_endpoint(0, &mtp_ep_data[MTP_IN_EP_IDX]); + usbd_add_endpoint(0, &mtp_ep_data[MTP_INT_EP_IDX]); s_mtp_status = USB_MTP_STOPPING; diff --git a/examples/device/cherryusb_device_mtp/main/device_mtp_main.c b/examples/device/cherryusb_device_mtp/main/device_mtp_main.c index 5e73e18..9ea5f2b 100644 --- a/examples/device/cherryusb_device_mtp/main/device_mtp_main.c +++ b/examples/device/cherryusb_device_mtp/main/device_mtp_main.c @@ -87,9 +87,9 @@ const uint8_t mtp_descriptor[] = { '2', 0x00, /* wcChar0 */ '0', 0x00, /* wcChar1 */ '2', 0x00, /* wcChar2 */ - '1', 0x00, /* wcChar3 */ + '4', 0x00, /* wcChar3 */ '0', 0x00, /* wcChar4 */ - '3', 0x00, /* wcChar5 */ + '6', 0x00, /* wcChar5 */ '1', 0x00, /* wcChar6 */ '0', 0x00, /* wcChar7 */ '0', 0x00, /* wcChar8 */ @@ -112,7 +112,7 @@ const uint8_t mtp_descriptor[] = { 0x00 }; -void usbd_event_handler(uint8_t event) +void usbd_event_handler(uint8_t busid, uint8_t event) { switch (event) { case USBD_EVENT_RESET: @@ -144,19 +144,19 @@ void app_main(void) void sd_main(void); sd_main(); uint32_t before = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); - usbd_desc_register(mtp_descriptor); - usbd_add_interface(usbd_mtp_init_intf(&intf0, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP)); - usbd_initialize(); + usbd_desc_register(0, mtp_descriptor); + usbd_add_interface(0, usbd_mtp_init_intf(&intf0, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP)); + usbd_initialize(0, ESP_USBD_BASE, usbd_event_handler); while (1){ vTaskDelay(10000 / portTICK_PERIOD_MS); usbd_mtp_deinit(); - usbd_deinitialize(); + usbd_deinitialize(0); vTaskDelay(500 / portTICK_PERIOD_MS); uint32_t now; now = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); ESP_LOGW(TAG, "use %"PRIu32, before - now); - usbd_desc_register(mtp_descriptor); - usbd_add_interface(usbd_mtp_init_intf(&intf0, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP)); - usbd_initialize(); + usbd_desc_register(0, mtp_descriptor); + usbd_add_interface(0, usbd_mtp_init_intf(&intf0, CDC_OUT_EP, CDC_IN_EP, CDC_INT_EP)); + usbd_initialize(0, ESP_USBD_BASE, usbd_event_handler); } } \ No newline at end of file