diff --git a/Kconfig b/Kconfig index 0084b833..ce39f3e7 100644 --- a/Kconfig +++ b/Kconfig @@ -118,6 +118,11 @@ if CHERRYUSB prompt "Enable usb cdc ncm device" default n + config CHERRYUSB_DEVICE_MTP + bool + prompt "Enable usb mtp device, it is commercial charge" + default n + config CHERRYUSB_DEVICE_DFU bool prompt "Enable usb dfu device" diff --git a/Kconfig.rtt b/Kconfig.rtt index 4c1586e2..209332f5 100644 --- a/Kconfig.rtt +++ b/Kconfig.rtt @@ -120,6 +120,11 @@ if RT_USING_CHERRYUSB prompt "Enable usb cdc ncm device" default n + config RT_CHERRYUSB_DEVICE_MTP + bool + prompt "Enable usb mtp device, it is commercial charge" + default n + config RT_CHERRYUSB_DEVICE_DFU bool prompt "Enable usb dfu device" diff --git a/Kconfig.rttpkg b/Kconfig.rttpkg index 3663f0fb..73d5f3b2 100644 --- a/Kconfig.rttpkg +++ b/Kconfig.rttpkg @@ -119,6 +119,11 @@ if PKG_USING_CHERRYUSB prompt "Enable usb cdc ncm device" default n + config PKG_CHERRYUSB_DEVICE_MTP + bool + prompt "Enable usb mtp device, it is commercial charge" + default n + config PKG_CHERRYUSB_DEVICE_DFU bool prompt "Enable usb dfu device" diff --git a/README.md b/README.md index 38d5cd28..ec0077cb 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ CherryUSB Device Stack has the following functions: - Support Device Firmware Upgrade CLASS (DFU) - Support USB MIDI CLASS (MIDI) - Support Remote NDIS (RNDIS) +- Support Media Transfer Protocol (MTP) - Support WINUSB1.0, WINUSB2.0, WEBUSB, BOS - Support Vendor class - Support UF2 @@ -88,6 +89,7 @@ CherryUSB Device Stack resource usage (GCC 10.2 with -O2): |usbd_audio.c | ~1500 | 0 | 0 | 0 | |usbd_video.c | ~3700 | 0 | 132 * bus | 0 | |usbd_rndis.c | ~4000 | 2 * 1580(default)+156+8 | 80 | 0 | +|usbd_mtp.c | ~9000 | 2048(default)+128 | sizeof(struct mtp_object) * n| 0 | ## Host Stack Overview diff --git a/README_zh.md b/README_zh.md index 98f44d8f..11ca4af3 100644 --- a/README_zh.md +++ b/README_zh.md @@ -71,6 +71,7 @@ CherryUSB Device 协议栈当前实现以下功能: - 支持 Device Firmware Upgrade CLASS (DFU) - 支持 USB MIDI CLASS (MIDI) - 支持 Remote NDIS (RNDIS) +- 支持 Media Transfer Protocol (MTP) - 支持 WINUSB1.0、WINUSB2.0、WEBUSB、BOS - 支持 Vendor 类 class - 支持 UF2 @@ -88,6 +89,7 @@ CherryUSB Device 协议栈资源占用说明(GCC 10.2 with -O2): |usbd_audio.c | ~1500 | 0 | 0 | 0 | |usbd_video.c | ~3700 | 0 | 132 * bus | 0 | |usbd_rndis.c | ~4000 | 2 * 1580(default)+156+8 | 80 | 0 | +|usbd_mtp.c | ~9000 | 2048(default)+128 | sizeof(struct mtp_object) * n| 0 | ## Host 协议栈简介 diff --git a/cherryusb_config_template.h b/cherryusb_config_template.h index 8f2f5652..55fcb4b4 100644 --- a/cherryusb_config_template.h +++ b/cherryusb_config_template.h @@ -107,6 +107,28 @@ #define CONFIG_USBDEV_MSC_STACKSIZE 2048 #endif +#ifndef CONFIG_USBDEV_MTP_MAX_BUFSIZE +#define CONFIG_USBDEV_MTP_MAX_BUFSIZE 2048 +#endif + +#ifndef CONFIG_USBDEV_MTP_MAX_OBJECTS +#define CONFIG_USBDEV_MTP_MAX_OBJECTS 256 +#endif + +#ifndef CONFIG_USBDEV_MTP_MAX_PATHNAME +#define CONFIG_USBDEV_MTP_MAX_PATHNAME 256 +#endif + +#define CONFIG_USBDEV_MTP_THREAD + +#ifndef CONFIG_USBDEV_MTP_PRIO +#define CONFIG_USBDEV_MTP_PRIO 4 +#endif + +#ifndef CONFIG_USBDEV_MTP_STACKSIZE +#define CONFIG_USBDEV_MTP_STACKSIZE 4096 +#endif + #ifndef CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE #define CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE 156 #endif diff --git a/class/mtp/usb_mtp.h b/class/mtp/usb_mtp.h new file mode 100644 index 00000000..524abb75 --- /dev/null +++ b/class/mtp/usb_mtp.h @@ -0,0 +1,683 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (c) 2025, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USB_MTP_H +#define USB_MTP_H + +// clang-format off +#define MTP_STANDARD_VERSION 100 + +// Container Types +#define MTP_CONTAINER_TYPE_UNDEFINED 0 +#define MTP_CONTAINER_TYPE_COMMAND 1 +#define MTP_CONTAINER_TYPE_DATA 2 +#define MTP_CONTAINER_TYPE_RESPONSE 3 +#define MTP_CONTAINER_TYPE_EVENT 4 + +// Container Offsets +#define MTP_CONTAINER_LENGTH_OFFSET 0 +#define MTP_CONTAINER_TYPE_OFFSET 4 +#define MTP_CONTAINER_CODE_OFFSET 6 +#define MTP_CONTAINER_TRANSACTION_ID_OFFSET 8 +#define MTP_CONTAINER_PARAMETER_OFFSET 12 +#define MTP_CONTAINER_HEADER_SIZE 12 + +// Maximum buffer size for a MTP packet. +#define MTP_BUFFER_SIZE 16384 + +// MTP Data Types +#define MTP_TYPE_UNDEFINED 0x0000 // Undefined +#define MTP_TYPE_INT8 0x0001 // Signed 8-bit integer +#define MTP_TYPE_UINT8 0x0002 // Unsigned 8-bit integer +#define MTP_TYPE_INT16 0x0003 // Signed 16-bit integer +#define MTP_TYPE_UINT16 0x0004 // Unsigned 16-bit integer +#define MTP_TYPE_INT32 0x0005 // Signed 32-bit integer +#define MTP_TYPE_UINT32 0x0006 // Unsigned 32-bit integer +#define MTP_TYPE_INT64 0x0007 // Signed 64-bit integer +#define MTP_TYPE_UINT64 0x0008 // Unsigned 64-bit integer +#define MTP_TYPE_INT128 0x0009 // Signed 128-bit integer +#define MTP_TYPE_UINT128 0x000A // Unsigned 128-bit integer +#define MTP_TYPE_AINT8 0x4001 // Array of signed 8-bit integers +#define MTP_TYPE_AUINT8 0x4002 // Array of unsigned 8-bit integers +#define MTP_TYPE_AINT16 0x4003 // Array of signed 16-bit integers +#define MTP_TYPE_AUINT16 0x4004 // Array of unsigned 16-bit integers +#define MTP_TYPE_AINT32 0x4005 // Array of signed 32-bit integers +#define MTP_TYPE_AUINT32 0x4006 // Array of unsigned 32-bit integers +#define MTP_TYPE_AINT64 0x4007 // Array of signed 64-bit integers +#define MTP_TYPE_AUINT64 0x4008 // Array of unsigned 64-bit integers +#define MTP_TYPE_AINT128 0x4009 // Array of signed 128-bit integers +#define MTP_TYPE_AUINT128 0x400A // Array of unsigned 128-bit integers +#define MTP_TYPE_STR 0xFFFF // Variable-length Unicode string + +// MTP Format Codes +#define MTP_FORMAT_UNDEFINED 0x3000 // Undefined object +#define MTP_FORMAT_ASSOCIATION 0x3001 // Association (for example, a folder) +#define MTP_FORMAT_SCRIPT 0x3002 // Device model-specific script +#define MTP_FORMAT_EXECUTABLE 0x3003 // Device model-specific binary executable +#define MTP_FORMAT_TEXT 0x3004 // Text file +#define MTP_FORMAT_HTML 0x3005 // Hypertext Markup Language file (text) +#define MTP_FORMAT_DPOF 0x3006 // Digital Print Order Format file (text) +#define MTP_FORMAT_AIFF 0x3007 // Audio clip +#define MTP_FORMAT_WAV 0x3008 // Audio clip +#define MTP_FORMAT_MP3 0x3009 // Audio clip +#define MTP_FORMAT_AVI 0x300A // Video clip +#define MTP_FORMAT_MPEG 0x300B // Video clip +#define MTP_FORMAT_ASF 0x300C // Microsoft Advanced Streaming Format (video) +#define MTP_FORMAT_DEFINED 0x3800 // Unknown image object +#define MTP_FORMAT_EXIF_JPEG 0x3801 // Exchangeable File Format, JEIDA standard +#define MTP_FORMAT_TIFF_EP 0x3802 // Tag Image File Format for Electronic Photography +#define MTP_FORMAT_FLASHPIX 0x3803 // Structured Storage Image Format +#define MTP_FORMAT_BMP 0x3804 // Microsoft Windows Bitmap file +#define MTP_FORMAT_CIFF 0x3805 // Canon Camera Image File Format +#define MTP_FORMAT_GIF 0x3807 // Graphics Interchange Format +#define MTP_FORMAT_JFIF 0x3808 // JPEG File Interchange Format +#define MTP_FORMAT_CD 0x3809 // PhotoCD Image Pac +#define MTP_FORMAT_PICT 0x380A // Quickdraw Image Format +#define MTP_FORMAT_PNG 0x380B // Portable Network Graphics +#define MTP_FORMAT_TIFF 0x380D // Tag Image File Format +#define MTP_FORMAT_TIFF_IT 0x380E // Tag Image File Format for Information Technology (graphic arts) +#define MTP_FORMAT_JP2 0x380F // JPEG2000 Baseline File Format +#define MTP_FORMAT_JPX 0x3810 // JPEG2000 Extended File Format +#define MTP_FORMAT_DNG 0x3811 // Digital Negative +#define MTP_FORMAT_HEIF 0x3812 // HEIF images +#define MTP_FORMAT_UNDEFINED_FIRMWARE 0xB802 +#define MTP_FORMAT_WINDOWS_IMAGE_FORMAT 0xB881 +#define MTP_FORMAT_UNDEFINED_AUDIO 0xB900 +#define MTP_FORMAT_WMA 0xB901 +#define MTP_FORMAT_OGG 0xB902 +#define MTP_FORMAT_AAC 0xB903 +#define MTP_FORMAT_AUDIBLE 0xB904 +#define MTP_FORMAT_FLAC 0xB906 +#define MTP_FORMAT_UNDEFINED_VIDEO 0xB980 +#define MTP_FORMAT_WMV 0xB981 +#define MTP_FORMAT_MP4_CONTAINER 0xB982 // ISO 14496-1 +#define MTP_FORMAT_MP2 0xB983 +#define MTP_FORMAT_3GP_CONTAINER 0xB984 // 3GPP file format. Details: http://www.3gpp.org/ftp/Specs/html-info/26244.htm (page title - \u201cTransparent end-to-end packet switched streaming service, 3GPP file format\u201d). +#define MTP_FORMAT_UNDEFINED_COLLECTION 0xBA00 +#define MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM 0xBA01 +#define MTP_FORMAT_ABSTRACT_IMAGE_ALBUM 0xBA02 +#define MTP_FORMAT_ABSTRACT_AUDIO_ALBUM 0xBA03 +#define MTP_FORMAT_ABSTRACT_VIDEO_ALBUM 0xBA04 +#define MTP_FORMAT_ABSTRACT_AV_PLAYLIST 0xBA05 +#define MTP_FORMAT_ABSTRACT_CONTACT_GROUP 0xBA06 +#define MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER 0xBA07 +#define MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION 0xBA08 +#define MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST 0xBA09 +#define MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST 0xBA0A +#define MTP_FORMAT_ABSTRACT_MEDIACAST 0xBA0B // For use with mediacasts; references multimedia enclosures of RSS feeds or episodic content +#define MTP_FORMAT_WPL_PLAYLIST 0xBA10 +#define MTP_FORMAT_M3U_PLAYLIST 0xBA11 +#define MTP_FORMAT_MPL_PLAYLIST 0xBA12 +#define MTP_FORMAT_ASX_PLAYLIST 0xBA13 +#define MTP_FORMAT_PLS_PLAYLIST 0xBA14 +#define MTP_FORMAT_UNDEFINED_DOCUMENT 0xBA80 +#define MTP_FORMAT_ABSTRACT_DOCUMENT 0xBA81 +#define MTP_FORMAT_XML_DOCUMENT 0xBA82 +#define MTP_FORMAT_MS_WORD_DOCUMENT 0xBA83 +#define MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT 0xBA84 +#define MTP_FORMAT_MS_EXCEL_SPREADSHEET 0xBA85 +#define MTP_FORMAT_MS_POWERPOINT_PRESENTATION 0xBA86 +#define MTP_FORMAT_UNDEFINED_MESSAGE 0xBB00 +#define MTP_FORMAT_ABSTRACT_MESSSAGE 0xBB01 +#define MTP_FORMAT_UNDEFINED_CONTACT 0xBB80 +#define MTP_FORMAT_ABSTRACT_CONTACT 0xBB81 +#define MTP_FORMAT_VCARD_2 0xBB82 + +// MTP Object Property Codes +#define MTP_PROPERTY_STORAGE_ID 0xDC01 +#define MTP_PROPERTY_OBJECT_FORMAT 0xDC02 +#define MTP_PROPERTY_PROTECTION_STATUS 0xDC03 +#define MTP_PROPERTY_OBJECT_SIZE 0xDC04 +#define MTP_PROPERTY_ASSOCIATION_TYPE 0xDC05 +#define MTP_PROPERTY_ASSOCIATION_DESC 0xDC06 +#define MTP_PROPERTY_OBJECT_FILE_NAME 0xDC07 +#define MTP_PROPERTY_DATE_CREATED 0xDC08 +#define MTP_PROPERTY_DATE_MODIFIED 0xDC09 +#define MTP_PROPERTY_KEYWORDS 0xDC0A +#define MTP_PROPERTY_PARENT_OBJECT 0xDC0B +#define MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS 0xDC0C +#define MTP_PROPERTY_HIDDEN 0xDC0D +#define MTP_PROPERTY_SYSTEM_OBJECT 0xDC0E +#define MTP_PROPERTY_PERSISTENT_UID 0xDC41 +#define MTP_PROPERTY_SYNC_ID 0xDC42 +#define MTP_PROPERTY_PROPERTY_BAG 0xDC43 +#define MTP_PROPERTY_NAME 0xDC44 +#define MTP_PROPERTY_CREATED_BY 0xDC45 +#define MTP_PROPERTY_ARTIST 0xDC46 +#define MTP_PROPERTY_DATE_AUTHORED 0xDC47 +#define MTP_PROPERTY_DESCRIPTION 0xDC48 +#define MTP_PROPERTY_URL_REFERENCE 0xDC49 +#define MTP_PROPERTY_LANGUAGE_LOCALE 0xDC4A +#define MTP_PROPERTY_COPYRIGHT_INFORMATION 0xDC4B +#define MTP_PROPERTY_SOURCE 0xDC4C +#define MTP_PROPERTY_ORIGIN_LOCATION 0xDC4D +#define MTP_PROPERTY_DATE_ADDED 0xDC4E +#define MTP_PROPERTY_NON_CONSUMABLE 0xDC4F +#define MTP_PROPERTY_CORRUPT_UNPLAYABLE 0xDC50 +#define MTP_PROPERTY_PRODUCER_SERIAL_NUMBER 0xDC51 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT 0xDC81 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE 0xDC82 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT 0xDC83 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH 0xDC84 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION 0xDC85 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA 0xDC86 +#define MTP_PROPERTY_WIDTH 0xDC87 +#define MTP_PROPERTY_HEIGHT 0xDC88 +#define MTP_PROPERTY_DURATION 0xDC89 +#define MTP_PROPERTY_RATING 0xDC8A +#define MTP_PROPERTY_TRACK 0xDC8B +#define MTP_PROPERTY_GENRE 0xDC8C +#define MTP_PROPERTY_CREDITS 0xDC8D +#define MTP_PROPERTY_LYRICS 0xDC8E +#define MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID 0xDC8F +#define MTP_PROPERTY_PRODUCED_BY 0xDC90 +#define MTP_PROPERTY_USE_COUNT 0xDC91 +#define MTP_PROPERTY_SKIP_COUNT 0xDC92 +#define MTP_PROPERTY_LAST_ACCESSED 0xDC93 +#define MTP_PROPERTY_PARENTAL_RATING 0xDC94 +#define MTP_PROPERTY_META_GENRE 0xDC95 +#define MTP_PROPERTY_COMPOSER 0xDC96 +#define MTP_PROPERTY_EFFECTIVE_RATING 0xDC97 +#define MTP_PROPERTY_SUBTITLE 0xDC98 +#define MTP_PROPERTY_ORIGINAL_RELEASE_DATE 0xDC99 +#define MTP_PROPERTY_ALBUM_NAME 0xDC9A +#define MTP_PROPERTY_ALBUM_ARTIST 0xDC9B +#define MTP_PROPERTY_MOOD 0xDC9C +#define MTP_PROPERTY_DRM_STATUS 0xDC9D +#define MTP_PROPERTY_SUB_DESCRIPTION 0xDC9E +#define MTP_PROPERTY_IS_CROPPED 0xDCD1 +#define MTP_PROPERTY_IS_COLOUR_CORRECTED 0xDCD2 +#define MTP_PROPERTY_IMAGE_BIT_DEPTH 0xDCD3 +#define MTP_PROPERTY_F_NUMBER 0xDCD4 +#define MTP_PROPERTY_EXPOSURE_TIME 0xDCD5 +#define MTP_PROPERTY_EXPOSURE_INDEX 0xDCD6 +#define MTP_PROPERTY_TOTAL_BITRATE 0xDE91 +#define MTP_PROPERTY_BITRATE_TYPE 0xDE92 +#define MTP_PROPERTY_SAMPLE_RATE 0xDE93 +#define MTP_PROPERTY_NUMBER_OF_CHANNELS 0xDE94 +#define MTP_PROPERTY_AUDIO_BIT_DEPTH 0xDE95 +#define MTP_PROPERTY_SCAN_TYPE 0xDE97 +#define MTP_PROPERTY_AUDIO_WAVE_CODEC 0xDE99 +#define MTP_PROPERTY_AUDIO_BITRATE 0xDE9A +#define MTP_PROPERTY_VIDEO_FOURCC_CODEC 0xDE9B +#define MTP_PROPERTY_VIDEO_BITRATE 0xDE9C +#define MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS 0xDE9D +#define MTP_PROPERTY_KEYFRAME_DISTANCE 0xDE9E +#define MTP_PROPERTY_BUFFER_SIZE 0xDE9F +#define MTP_PROPERTY_ENCODING_QUALITY 0xDEA0 +#define MTP_PROPERTY_ENCODING_PROFILE 0xDEA1 +#define MTP_PROPERTY_DISPLAY_NAME 0xDCE0 +#define MTP_PROPERTY_BODY_TEXT 0xDCE1 +#define MTP_PROPERTY_SUBJECT 0xDCE2 +#define MTP_PROPERTY_PRIORITY 0xDCE3 +#define MTP_PROPERTY_GIVEN_NAME 0xDD00 +#define MTP_PROPERTY_MIDDLE_NAMES 0xDD01 +#define MTP_PROPERTY_FAMILY_NAME 0xDD02 +#define MTP_PROPERTY_PREFIX 0xDD03 +#define MTP_PROPERTY_SUFFIX 0xDD04 +#define MTP_PROPERTY_PHONETIC_GIVEN_NAME 0xDD05 +#define MTP_PROPERTY_PHONETIC_FAMILY_NAME 0xDD06 +#define MTP_PROPERTY_EMAIL_PRIMARY 0xDD07 +#define MTP_PROPERTY_EMAIL_PERSONAL_1 0xDD08 +#define MTP_PROPERTY_EMAIL_PERSONAL_2 0xDD09 +#define MTP_PROPERTY_EMAIL_BUSINESS_1 0xDD0A +#define MTP_PROPERTY_EMAIL_BUSINESS_2 0xDD0B +#define MTP_PROPERTY_EMAIL_OTHERS 0xDD0C +#define MTP_PROPERTY_PHONE_NUMBER_PRIMARY 0xDD0D +#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL 0xDD0E +#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2 0xDD0F +#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS 0xDD10 +#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2 0xDD11 +#define MTP_PROPERTY_PHONE_NUMBER_MOBILE 0xDD12 +#define MTP_PROPERTY_PHONE_NUMBER_MOBILE_2 0xDD13 +#define MTP_PROPERTY_FAX_NUMBER_PRIMARY 0xDD14 +#define MTP_PROPERTY_FAX_NUMBER_PERSONAL 0xDD15 +#define MTP_PROPERTY_FAX_NUMBER_BUSINESS 0xDD16 +#define MTP_PROPERTY_PAGER_NUMBER 0xDD17 +#define MTP_PROPERTY_PHONE_NUMBER_OTHERS 0xDD18 +#define MTP_PROPERTY_PRIMARY_WEB_ADDRESS 0xDD19 +#define MTP_PROPERTY_PERSONAL_WEB_ADDRESS 0xDD1A +#define MTP_PROPERTY_BUSINESS_WEB_ADDRESS 0xDD1B +#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS 0xDD1C +#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2 0xDD1D +#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3 0xDD1E +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL 0xDD1F +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1 0xDD20 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2 0xDD21 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY 0xDD22 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION 0xDD23 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE 0xDD24 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY 0xDD25 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL 0xDD26 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1 0xDD27 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2 0xDD28 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY 0xDD29 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION 0xDD2A +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE 0xDD2B +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY 0xDD2C +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL 0xDD2D +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1 0xDD2E +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2 0xDD2F +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY 0xDD30 +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION 0xDD31 +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE 0xDD32 +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY 0xDD33 +#define MTP_PROPERTY_ORGANIZATION_NAME 0xDD34 +#define MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME 0xDD35 +#define MTP_PROPERTY_ROLE 0xDD36 +#define MTP_PROPERTY_BIRTHDATE 0xDD37 +#define MTP_PROPERTY_MESSAGE_TO 0xDD40 +#define MTP_PROPERTY_MESSAGE_CC 0xDD41 +#define MTP_PROPERTY_MESSAGE_BCC 0xDD42 +#define MTP_PROPERTY_MESSAGE_READ 0xDD43 +#define MTP_PROPERTY_MESSAGE_RECEIVED_TIME 0xDD44 +#define MTP_PROPERTY_MESSAGE_SENDER 0xDD45 +#define MTP_PROPERTY_ACTIVITY_BEGIN_TIME 0xDD50 +#define MTP_PROPERTY_ACTIVITY_END_TIME 0xDD51 +#define MTP_PROPERTY_ACTIVITY_LOCATION 0xDD52 +#define MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES 0xDD54 +#define MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES 0xDD55 +#define MTP_PROPERTY_ACTIVITY_RESOURCES 0xDD56 +#define MTP_PROPERTY_ACTIVITY_ACCEPTED 0xDD57 +#define MTP_PROPERTY_ACTIVITY_TENTATIVE 0xDD58 +#define MTP_PROPERTY_ACTIVITY_DECLINED 0xDD59 +#define MTP_PROPERTY_ACTIVITY_REMAINDER_TIME 0xDD5A +#define MTP_PROPERTY_ACTIVITY_OWNER 0xDD5B +#define MTP_PROPERTY_ACTIVITY_STATUS 0xDD5C +#define MTP_PROPERTY_OWNER 0xDD5D +#define MTP_PROPERTY_EDITOR 0xDD5E +#define MTP_PROPERTY_WEBMASTER 0xDD5F +#define MTP_PROPERTY_URL_SOURCE 0xDD60 +#define MTP_PROPERTY_URL_DESTINATION 0xDD61 +#define MTP_PROPERTY_TIME_BOOKMARK 0xDD62 +#define MTP_PROPERTY_OBJECT_BOOKMARK 0xDD63 +#define MTP_PROPERTY_BYTE_BOOKMARK 0xDD64 +#define MTP_PROPERTY_LAST_BUILD_DATE 0xDD70 +#define MTP_PROPERTY_TIME_TO_LIVE 0xDD71 +#define MTP_PROPERTY_MEDIA_GUID 0xDD72 + +// MTP Device Property Codes +#define MTP_DEVICE_PROPERTY_UNDEFINED 0x5000 +#define MTP_DEVICE_PROPERTY_BATTERY_LEVEL 0x5001 +#define MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE 0x5002 +#define MTP_DEVICE_PROPERTY_IMAGE_SIZE 0x5003 +#define MTP_DEVICE_PROPERTY_COMPRESSION_SETTING 0x5004 +#define MTP_DEVICE_PROPERTY_WHITE_BALANCE 0x5005 +#define MTP_DEVICE_PROPERTY_RGB_GAIN 0x5006 +#define MTP_DEVICE_PROPERTY_F_NUMBER 0x5007 +#define MTP_DEVICE_PROPERTY_FOCAL_LENGTH 0x5008 +#define MTP_DEVICE_PROPERTY_FOCUS_DISTANCE 0x5009 +#define MTP_DEVICE_PROPERTY_FOCUS_MODE 0x500A +#define MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE 0x500B +#define MTP_DEVICE_PROPERTY_FLASH_MODE 0x500C +#define MTP_DEVICE_PROPERTY_EXPOSURE_TIME 0x500D +#define MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE 0x500E +#define MTP_DEVICE_PROPERTY_EXPOSURE_INDEX 0x500F +#define MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION 0x5010 +#define MTP_DEVICE_PROPERTY_DATETIME 0x5011 +#define MTP_DEVICE_PROPERTY_CAPTURE_DELAY 0x5012 +#define MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE 0x5013 +#define MTP_DEVICE_PROPERTY_CONTRAST 0x5014 +#define MTP_DEVICE_PROPERTY_SHARPNESS 0x5015 +#define MTP_DEVICE_PROPERTY_DIGITAL_ZOOM 0x5016 +#define MTP_DEVICE_PROPERTY_EFFECT_MODE 0x5017 +#define MTP_DEVICE_PROPERTY_BURST_NUMBER 0x5018 +#define MTP_DEVICE_PROPERTY_BURST_INTERVAL 0x5019 +#define MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER 0x501A +#define MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL 0x501B +#define MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE 0x501C +#define MTP_DEVICE_PROPERTY_UPLOAD_URL 0x501D +#define MTP_DEVICE_PROPERTY_ARTIST 0x501E +#define MTP_DEVICE_PROPERTY_COPYRIGHT_INFO 0x501F +#define MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER 0xD401 +#define MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME 0xD402 +#define MTP_DEVICE_PROPERTY_VOLUME 0xD403 +#define MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED 0xD404 +#define MTP_DEVICE_PROPERTY_DEVICE_ICON 0xD405 +#define MTP_DEVICE_PROPERTY_PLAYBACK_RATE 0xD410 +#define MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT 0xD411 +#define MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX 0xD412 +#define MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO 0xD406 +#define MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE 0xD407 + +// MTP Operation Codes +#define MTP_OPERATION_GET_DEVICE_INFO 0x1001 +#define MTP_OPERATION_OPEN_SESSION 0x1002 +#define MTP_OPERATION_CLOSE_SESSION 0x1003 +#define MTP_OPERATION_GET_STORAGE_IDS 0x1004 +#define MTP_OPERATION_GET_STORAGE_INFO 0x1005 +#define MTP_OPERATION_GET_NUM_OBJECTS 0x1006 +#define MTP_OPERATION_GET_OBJECT_HANDLES 0x1007 +#define MTP_OPERATION_GET_OBJECT_INFO 0x1008 +#define MTP_OPERATION_GET_OBJECT 0x1009 +#define MTP_OPERATION_GET_THUMB 0x100A +#define MTP_OPERATION_DELETE_OBJECT 0x100B +#define MTP_OPERATION_SEND_OBJECT_INFO 0x100C +#define MTP_OPERATION_SEND_OBJECT 0x100D +#define MTP_OPERATION_INITIATE_CAPTURE 0x100E +#define MTP_OPERATION_FORMAT_STORE 0x100F +#define MTP_OPERATION_RESET_DEVICE 0x1010 +#define MTP_OPERATION_SELF_TEST 0x1011 +#define MTP_OPERATION_SET_OBJECT_PROTECTION 0x1012 +#define MTP_OPERATION_POWER_DOWN 0x1013 +#define MTP_OPERATION_GET_DEVICE_PROP_DESC 0x1014 +#define MTP_OPERATION_GET_DEVICE_PROP_VALUE 0x1015 +#define MTP_OPERATION_SET_DEVICE_PROP_VALUE 0x1016 +#define MTP_OPERATION_RESET_DEVICE_PROP_VALUE 0x1017 +#define MTP_OPERATION_TERMINATE_OPEN_CAPTURE 0x1018 +#define MTP_OPERATION_MOVE_OBJECT 0x1019 +#define MTP_OPERATION_COPY_OBJECT 0x101A +#define MTP_OPERATION_GET_PARTIAL_OBJECT 0x101B +#define MTP_OPERATION_INITIATE_OPEN_CAPTURE 0x101C +#define MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED 0x9801 +#define MTP_OPERATION_GET_OBJECT_PROP_DESC 0x9802 +#define MTP_OPERATION_GET_OBJECT_PROP_VALUE 0x9803 +#define MTP_OPERATION_SET_OBJECT_PROP_VALUE 0x9804 +#define MTP_OPERATION_GET_OBJECT_PROP_LIST 0x9805 +#define MTP_OPERATION_SET_OBJECT_PROP_LIST 0x9806 +#define MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC 0x9807 +#define MTP_OPERATION_SEND_OBJECT_PROP_LIST 0x9808 +#define MTP_OPERATION_GET_OBJECT_REFERENCES 0x9810 +#define MTP_OPERATION_SET_OBJECT_REFERENCES 0x9811 +#define MTP_OPERATION_SKIP 0x9820 + +// Android extensions for direct file IO + +// Same as GetPartialObject, but with 64 bit offset +#define MTP_OPERATION_GET_PARTIAL_OBJECT_64 0x95C1 +// Same as GetPartialObject64, but copying host to device +#define MTP_OPERATION_SEND_PARTIAL_OBJECT 0x95C2 +// Truncates file to 64 bit length +#define MTP_OPERATION_TRUNCATE_OBJECT 0x95C3 +// Must be called before using SendPartialObject and TruncateObject +#define MTP_OPERATION_BEGIN_EDIT_OBJECT 0x95C4 +// Called to commit changes made by SendPartialObject and TruncateObject +#define MTP_OPERATION_END_EDIT_OBJECT 0x95C5 + +// MTP Response Codes +#define MTP_RESPONSE_UNDEFINED 0x2000 +#define MTP_RESPONSE_OK 0x2001 +#define MTP_RESPONSE_GENERAL_ERROR 0x2002 +#define MTP_RESPONSE_SESSION_NOT_OPEN 0x2003 +#define MTP_RESPONSE_INVALID_TRANSACTION_ID 0x2004 +#define MTP_RESPONSE_OPERATION_NOT_SUPPORTED 0x2005 +#define MTP_RESPONSE_PARAMETER_NOT_SUPPORTED 0x2006 +#define MTP_RESPONSE_INCOMPLETE_TRANSFER 0x2007 +#define MTP_RESPONSE_INVALID_STORAGE_ID 0x2008 +#define MTP_RESPONSE_INVALID_OBJECT_HANDLE 0x2009 +#define MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED 0x200A +#define MTP_RESPONSE_INVALID_OBJECT_FORMAT_CODE 0x200B +#define MTP_RESPONSE_STORAGE_FULL 0x200C +#define MTP_RESPONSE_OBJECT_WRITE_PROTECTED 0x200D +#define MTP_RESPONSE_STORE_READ_ONLY 0x200E +#define MTP_RESPONSE_ACCESS_DENIED 0x200F +#define MTP_RESPONSE_NO_THUMBNAIL_PRESENT 0x2010 +#define MTP_RESPONSE_SELF_TEST_FAILED 0x2011 +#define MTP_RESPONSE_PARTIAL_DELETION 0x2012 +#define MTP_RESPONSE_STORE_NOT_AVAILABLE 0x2013 +#define MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED 0x2014 +#define MTP_RESPONSE_NO_VALID_OBJECT_INFO 0x2015 +#define MTP_RESPONSE_INVALID_CODE_FORMAT 0x2016 +#define MTP_RESPONSE_UNKNOWN_VENDOR_CODE 0x2017 +#define MTP_RESPONSE_CAPTURE_ALREADY_TERMINATED 0x2018 +#define MTP_RESPONSE_DEVICE_BUSY 0x2019 +#define MTP_RESPONSE_INVALID_PARENT_OBJECT 0x201A +#define MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT 0x201B +#define MTP_RESPONSE_INVALID_DEVICE_PROP_VALUE 0x201C +#define MTP_RESPONSE_INVALID_PARAMETER 0x201D +#define MTP_RESPONSE_SESSION_ALREADY_OPEN 0x201E +#define MTP_RESPONSE_TRANSACTION_CANCELLED 0x201F +#define MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED 0x2020 +#define MTP_RESPONSE_INVALID_OBJECT_PROP_CODE 0xA801 +#define MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT 0xA802 +#define MTP_RESPONSE_INVALID_OBJECT_PROP_VALUE 0xA803 +#define MTP_RESPONSE_INVALID_OBJECT_REFERENCE 0xA804 +#define MTP_RESPONSE_GROUP_NOT_SUPPORTED 0xA805 +#define MTP_RESPONSE_INVALID_DATASET 0xA806 +#define MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED 0xA807 +#define MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED 0xA808 +#define MTP_RESPONSE_OBJECT_TOO_LARGE 0xA809 +#define MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED 0xA80A +#define MTP_RESPONSE_NO_RESPONSE 0xFFFF + +// MTP Event Codes +#define MTP_EVENT_UNDEFINED 0x4000 +#define MTP_EVENT_CANCEL_TRANSACTION 0x4001 +#define MTP_EVENT_OBJECT_ADDED 0x4002 +#define MTP_EVENT_OBJECT_REMOVED 0x4003 +#define MTP_EVENT_STORE_ADDED 0x4004 +#define MTP_EVENT_STORE_REMOVED 0x4005 +#define MTP_EVENT_DEVICE_PROP_CHANGED 0x4006 +#define MTP_EVENT_OBJECT_INFO_CHANGED 0x4007 +#define MTP_EVENT_DEVICE_INFO_CHANGED 0x4008 +#define MTP_EVENT_REQUEST_OBJECT_TRANSFER 0x4009 +#define MTP_EVENT_STORE_FULL 0x400A +#define MTP_EVENT_DEVICE_RESET 0x400B +#define MTP_EVENT_STORAGE_INFO_CHANGED 0x400C +#define MTP_EVENT_CAPTURE_COMPLETE 0x400D +#define MTP_EVENT_UNREPORTED_STATUS 0x400E +#define MTP_EVENT_OBJECT_PROP_CHANGED 0xC801 +#define MTP_EVENT_OBJECT_PROP_DESC_CHANGED 0xC802 +#define MTP_EVENT_OBJECT_REFERENCES_CHANGED 0xC803 + +// Storage Type +#define MTP_STORAGE_FIXED_ROM 0x0001 +#define MTP_STORAGE_REMOVABLE_ROM 0x0002 +#define MTP_STORAGE_FIXED_RAM 0x0003 +#define MTP_STORAGE_REMOVABLE_RAM 0x0004 + +// Storage File System +#define MTP_STORAGE_FILESYSTEM_FLAT 0x0001 +#define MTP_STORAGE_FILESYSTEM_HIERARCHICAL 0x0002 +#define MTP_STORAGE_FILESYSTEM_DCF 0x0003 + +// Storage Access Capability +#define MTP_STORAGE_READ_WRITE 0x0000 +#define MTP_STORAGE_READ_ONLY_WITHOUT_DELETE 0x0001 +#define MTP_STORAGE_READ_ONLY_WITH_DELETE 0x0002 + +// Association Type +#define MTP_ASSOCIATION_TYPE_UNDEFINED 0x0000 +#define MTP_ASSOCIATION_TYPE_GENERIC_FOLDER 0x0001 + +// MTP class reqeusts +#define MTP_REQUEST_CANCEL 0x64 +#define MTP_REQUEST_GET_EXT_EVENT_DATA 0x65 +#define MTP_REQUEST_RESET 0x66 +#define MTP_REQUEST_GET_DEVICE_STATUS 0x67 +// clang-format on + +#define USB_MTP_CLASS 0x06 + +#define USB_MTP_SUB_CLASS 0x01U +#define USB_MTP_PROTOCOL 0x01U + +struct mtp_header { + uint32_t conlen; + uint16_t contype; + uint16_t code; + uint32_t trans_id; + uint32_t param[]; +}; + +struct mtp_string { + uint8_t len; + uint16_t string[255]; +}; + +struct mtp_device_info { + uint16_t StandardVersion; + uint32_t VendorExtensionID; + uint16_t VendorExtensionVersion; + uint8_t VendorExtensionDesc_len; + uint16_t VendorExtensionDesc[255]; + uint16_t FunctionalMode; + uint32_t OperationsSupported_len; + uint16_t OperationsSupported[255]; + uint32_t EventsSupported_len; + uint16_t EventsSupported[255]; + uint32_t DevicePropertiesSupported_len; + uint16_t DevicePropertiesSupported[255]; + uint32_t CaptureFormats_len; + uint16_t CaptureFormats[255]; + uint32_t ImageFormats_len; + uint16_t ImageFormats[255]; + uint8_t Manufacturer_len; + uint16_t Manufacturer[255]; + uint8_t Model_len; + uint16_t Model[255]; + uint8_t DeviceVersion_len; + uint16_t DeviceVersion[255]; + uint8_t SerialNumber_len; + uint16_t SerialNumber[255]; +} __PACKED; + +struct mtp_object_props_support { + uint32_t ObjectPropCode_len; + uint16_t ObjectPropCode[255]; +} __PACKED; + +struct mtp_device_prop_desc { + uint16_t DevicePropertyCode; + uint16_t DataType; + uint8_t GetSet; + uint16_t DefaultValue[1]; + uint16_t CurrentValue[1]; + uint8_t FormFlag; +} __PACKED; + +struct mtp_storage_id { + uint32_t StorageIDS_len; + uint32_t StorageIDS[255]; +} __PACKED; + +struct mtp_storage_info { + uint16_t StorageType; + uint16_t FilesystemType; + uint16_t AccessCapability; + uint64_t MaxCapability; + uint64_t FreeSpaceInBytes; + uint32_t FreeSpaceInObjects; + uint8_t StorageDescription_len; + uint8_t StorageDescription[255]; + uint8_t VolumeIdentifier_len; + uint8_t VolumeIdentifier[255]; +} __PACKED; + +struct mtp_object_handles { + uint32_t ObjectHandle_len; + uint32_t ObjectHandle[255]; +} __PACKED; + +struct mtp_object_prop_desc { + uint16_t ObjectPropertyCode; + uint16_t DataType; + uint8_t GetSet; + uint8_t DefValue[16]; + uint32_t GroupCode; + uint8_t FormFlag; +} __PACKED; + +struct mtp_object_prop_element { + uint32_t ObjectHandle; + uint16_t PropertyCode; + uint16_t Datatype; + uint8_t value[8]; +} __PACKED; + +struct mtp_object_prop_list { + uint32_t element_len; + struct mtp_object_prop_element element[1]; +} __PACKED; + +struct mtp_object_info { + uint32_t StorageId; + uint16_t ObjectFormat; + uint16_t ProtectionStatus; + uint32_t ObjectCompressedSize; + uint16_t ThumbFormat; + uint32_t ThumbCompressedSize; + uint32_t ThumbPixWidth; + uint32_t ThumbPixHeight; + uint32_t ImagePixWidth; + uint32_t ImagePixHeight; + uint32_t ImageBitDepth; + uint32_t ParentObject; + uint16_t AssociationType; + uint32_t AssociationDesc; + uint32_t SequenceNumber; + uint8_t Filename_len; + uint16_t Filename[CONFIG_USBDEV_MTP_MAX_PATHNAME]; + uint8_t CaptureDate[6]; + uint8_t ModificationDate[6]; +} __PACKED; + +struct mtp_object { + uint32_t storage_id; + uint32_t handle; + uint32_t parent_handle; + uint16_t format; + bool is_dir; + bool is_readonly; + bool is_hidden; + uint32_t file_size; + uint32_t file_full_name_length; + char file_full_name[CONFIG_USBDEV_MTP_MAX_PATHNAME]; + bool in_use; +}; + +/*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_DEVICE_CLASS_IMAGE, /* 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, /* wMaxPacketSize */ \ + 0x00, /* bInterval */ \ + 0x06 /* bLength */ +// clang-format on + +#endif \ No newline at end of file diff --git a/class/mtp/usbd_mtp.h b/class/mtp/usbd_mtp.h new file mode 100644 index 00000000..a189d524 --- /dev/null +++ b/class/mtp/usbd_mtp.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBD_MTP_H +#define USBD_MTP_H + +#include "usb_mtp.h" +#include +#include + +/* gcc toolchain does not implement dirent.h, so we define our own MTP_DIR and mtp_dirent */ + +typedef void MTP_DIR; + +struct mtp_statfs { + size_t f_bsize; /* block size */ + size_t f_blocks; /* total data blocks in file system */ + size_t f_bfree; /* free blocks in file system */ +}; + +struct mtp_dirent { + uint8_t d_type; /* The type of the file */ + uint8_t d_namlen; /* The length of the not including the terminating null file name */ + uint16_t d_reclen; /* length of this record */ + char d_name[CONFIG_USBDEV_MTP_MAX_PATHNAME]; /* The null-terminated file name */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +const char *usbd_mtp_fs_root_path(void); +const char *usbd_mtp_fs_description(void); + +int usbd_mtp_mkdir(const char *path); +int usbd_mtp_rmdir(const char *path); +MTP_DIR *usbd_mtp_opendir(const char *name); +int usbd_mtp_closedir(MTP_DIR *d); +struct mtp_dirent *usbd_mtp_readdir(MTP_DIR *d); + +int usbd_mtp_statfs(const char *path, struct mtp_statfs *buf); +int usbd_mtp_stat(const char *file, struct stat *buf); + +int usbd_mtp_open(const char *path, uint8_t mode); +int usbd_mtp_close(int fd); +int usbd_mtp_read(int fd, void *buf, size_t len); +int usbd_mtp_write(int fd, const void *buf, size_t len); + +int usbd_mtp_unlink(const char *path); + +#ifdef __cplusplus +} +#endif + +#endif /* USBD_MTP_H */ diff --git a/class/mtp/usbd_mtp_support.h b/class/mtp/usbd_mtp_support.h new file mode 100644 index 00000000..9281ec39 --- /dev/null +++ b/class/mtp/usbd_mtp_support.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2025, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBD_MTP_SUPPORT_H +#define USBD_MTP_SUPPORT_H + +#define MTP_VERSION 100 + +typedef struct _profile_property { + uint16_t prop_code; + uint16_t data_type; + uint8_t getset; + uint64_t default_value; + uint32_t group_code; + uint8_t form_flag; + uint16_t format_code; +} profile_property; + +typedef struct _format_property { + uint16_t format_code; + uint16_t *properties; +} formats_property; + +const char mtp_extension_string[] = "microsoft.com: 1.0; android.com: 1.0;"; + +const uint16_t supported_op[] = { + 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_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_GET_PARTIAL_OBJECT ,//0x101B + 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_PROP_LIST ,//0x9805 + //MTP_OPERATION_GET_OBJECT_REFERENCES ,//0x9810 + //MTP_OPERATION_SET_OBJECT_REFERENCES ,//0x9811 + // MTP_OPERATION_GET_PARTIAL_OBJECT_64 ,//0x95C1 + // MTP_OPERATION_SEND_PARTIAL_OBJECT ,//0x95C2 + // MTP_OPERATION_TRUNCATE_OBJECT ,//0x95C3 + // MTP_OPERATION_BEGIN_EDIT_OBJECT ,//0x95C4 + // MTP_OPERATION_END_EDIT_OBJECT //0x95C5 +}; + +int supported_op_size = sizeof(supported_op); + +const uint16_t supported_event[] = { + MTP_EVENT_OBJECT_ADDED, // 0x4002 + MTP_EVENT_OBJECT_REMOVED, // 0x4003 + MTP_EVENT_STORE_ADDED, // 0x4004 + MTP_EVENT_STORE_REMOVED, // 0x4005 + MTP_EVENT_STORAGE_INFO_CHANGED, // 0x400C + MTP_EVENT_OBJECT_INFO_CHANGED, // 0x4007 + MTP_EVENT_DEVICE_PROP_CHANGED, // 0x4006 + MTP_EVENT_OBJECT_PROP_CHANGED // 0xC801 +}; + +int supported_event_size = sizeof(supported_event); + +const formats_property support_format_properties[] = { + // format code prop code + { MTP_FORMAT_UNDEFINED, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF } }, + { MTP_FORMAT_ASSOCIATION, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF } } +#if 0 + { MTP_FORMAT_TEXT , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_HTML , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_MP4_CONTAINER, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_ARTIST, MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_DURATION, MTP_PROPERTY_DESCRIPTION, MTP_PROPERTY_WIDTH, MTP_PROPERTY_HEIGHT, MTP_PROPERTY_DATE_AUTHORED, + 0xFFFF} + }, + { MTP_FORMAT_3GP_CONTAINER, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_ARTIST, MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_DURATION, MTP_PROPERTY_DESCRIPTION, MTP_PROPERTY_WIDTH, MTP_PROPERTY_HEIGHT, MTP_PROPERTY_DATE_AUTHORED, + 0xFFFF} + }, + { MTP_FORMAT_WAV , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED,MTP_PROPERTY_ARTIST,MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_ALBUM_ARTIST, MTP_PROPERTY_TRACK, MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_PROPERTY_GENRE, MTP_PROPERTY_COMPOSER, + MTP_PROPERTY_AUDIO_WAVE_CODEC, MTP_PROPERTY_BITRATE_TYPE, MTP_PROPERTY_AUDIO_BITRATE, MTP_PROPERTY_NUMBER_OF_CHANNELS,MTP_PROPERTY_SAMPLE_RATE, + 0xFFFF} + }, + { MTP_FORMAT_MP3 , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED,MTP_PROPERTY_ARTIST,MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_ALBUM_ARTIST, MTP_PROPERTY_TRACK, MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_PROPERTY_GENRE, MTP_PROPERTY_COMPOSER, + MTP_PROPERTY_AUDIO_WAVE_CODEC, MTP_PROPERTY_BITRATE_TYPE, MTP_PROPERTY_AUDIO_BITRATE, MTP_PROPERTY_NUMBER_OF_CHANNELS,MTP_PROPERTY_SAMPLE_RATE, + 0xFFFF} + }, + { MTP_FORMAT_MPEG , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_ARTIST, MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_DURATION, MTP_PROPERTY_DESCRIPTION, MTP_PROPERTY_WIDTH, MTP_PROPERTY_HEIGHT, MTP_PROPERTY_DATE_AUTHORED, + 0xFFFF} + }, + { MTP_FORMAT_EXIF_JPEG , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_DESCRIPTION, MTP_PROPERTY_WIDTH, + MTP_PROPERTY_HEIGHT, MTP_PROPERTY_DATE_AUTHORED, + 0xFFFF} + }, + { MTP_FORMAT_BMP , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_DESCRIPTION, MTP_PROPERTY_WIDTH, + MTP_PROPERTY_HEIGHT, MTP_PROPERTY_DATE_AUTHORED, + 0xFFFF} + }, + { MTP_FORMAT_GIF , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_DESCRIPTION, MTP_PROPERTY_WIDTH, + MTP_PROPERTY_HEIGHT, MTP_PROPERTY_DATE_AUTHORED, + 0xFFFF} + }, + { MTP_FORMAT_JFIF , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_DESCRIPTION, MTP_PROPERTY_WIDTH, + MTP_PROPERTY_HEIGHT, MTP_PROPERTY_DATE_AUTHORED, + 0xFFFF} + }, + { MTP_FORMAT_WMA , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_ARTIST, MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_ALBUM_ARTIST, MTP_PROPERTY_TRACK, MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_PROPERTY_DURATION, MTP_PROPERTY_DESCRIPTION, + MTP_PROPERTY_GENRE, MTP_PROPERTY_COMPOSER, MTP_PROPERTY_AUDIO_WAVE_CODEC, MTP_PROPERTY_BITRATE_TYPE, MTP_PROPERTY_AUDIO_BITRATE, + MTP_PROPERTY_NUMBER_OF_CHANNELS, MTP_PROPERTY_SAMPLE_RATE, + 0xFFFF} + }, + { MTP_FORMAT_OGG , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_ARTIST, MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_ALBUM_ARTIST, MTP_PROPERTY_TRACK, MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_PROPERTY_DURATION, MTP_PROPERTY_DESCRIPTION, + MTP_PROPERTY_GENRE, MTP_PROPERTY_COMPOSER, MTP_PROPERTY_AUDIO_WAVE_CODEC, MTP_PROPERTY_BITRATE_TYPE, MTP_PROPERTY_AUDIO_BITRATE, + MTP_PROPERTY_NUMBER_OF_CHANNELS, MTP_PROPERTY_SAMPLE_RATE, + 0xFFFF} + }, + { MTP_FORMAT_AAC , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_ARTIST, MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_ALBUM_ARTIST, MTP_PROPERTY_TRACK, MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_PROPERTY_DURATION, MTP_PROPERTY_DESCRIPTION, + MTP_PROPERTY_GENRE, MTP_PROPERTY_COMPOSER, MTP_PROPERTY_AUDIO_WAVE_CODEC, MTP_PROPERTY_BITRATE_TYPE, MTP_PROPERTY_AUDIO_BITRATE, + MTP_PROPERTY_NUMBER_OF_CHANNELS, MTP_PROPERTY_SAMPLE_RATE, + 0xFFFF} + }, + { MTP_FORMAT_ABSTRACT_AV_PLAYLIST, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_WPL_PLAYLIST, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_M3U_PLAYLIST, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_PLS_PLAYLIST, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_XML_DOCUMENT, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_FLAC , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_AVI , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_ARTIST, MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_DURATION, MTP_PROPERTY_DESCRIPTION, MTP_PROPERTY_WIDTH, MTP_PROPERTY_HEIGHT, MTP_PROPERTY_DATE_AUTHORED, + 0xFFFF} + }, + { MTP_FORMAT_ASF , (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, MTP_PROPERTY_ARTIST, MTP_PROPERTY_ALBUM_NAME, + MTP_PROPERTY_DURATION, MTP_PROPERTY_DESCRIPTION, MTP_PROPERTY_WIDTH, MTP_PROPERTY_HEIGHT, MTP_PROPERTY_DATE_AUTHORED, + 0xFFFF} + }, + { MTP_FORMAT_MS_WORD_DOCUMENT, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_MS_EXCEL_SPREADSHEET, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + }, + { MTP_FORMAT_MS_POWERPOINT_PRESENTATION, (uint16_t[]){ MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS, MTP_PROPERTY_OBJECT_SIZE, + MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED, MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, + MTP_PROPERTY_NAME, MTP_PROPERTY_DISPLAY_NAME, MTP_PROPERTY_DATE_CREATED, + 0xFFFF} + } +#endif + , + + { 0xFFFF, (uint16_t[]){ 0xFFFF } } + +}; + +const profile_property support_object_properties[] = { + // prop_code data_type getset default_value group_code form_flag format_code + { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, 0x00, 0x00000000, 0x000000001, 0x00, 0xFFFF }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_UNDEFINED, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_ASSOCIATION, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_TEXT, 0x000000000, 0x00, MTP_FORMAT_TEXT }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_HTML, 0x000000000, 0x00, MTP_FORMAT_HTML }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_WAV, 0x000000000, 0x00, MTP_FORMAT_WAV }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_MP3, 0x000000000, 0x00, MTP_FORMAT_MP3 }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_MPEG, 0x000000000, 0x00, MTP_FORMAT_MPEG }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_EXIF_JPEG, 0x000000000, 0x00, MTP_FORMAT_EXIF_JPEG }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_BMP, 0x000000000, 0x00, MTP_FORMAT_BMP }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_AIFF, 0x000000000, 0x00, MTP_FORMAT_AIFF }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_MPEG, 0x000000000, 0x00, MTP_FORMAT_MPEG }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_WMA, 0x000000000, 0x00, MTP_FORMAT_WMA }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_OGG, 0x000000000, 0x00, MTP_FORMAT_OGG }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_AAC, 0x000000000, 0x00, MTP_FORMAT_AAC }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_MP4_CONTAINER, 0x000000000, 0x00, MTP_FORMAT_MP4_CONTAINER }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_3GP_CONTAINER, 0x000000000, 0x00, MTP_FORMAT_3GP_CONTAINER }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_ABSTRACT_AV_PLAYLIST, 0x000000000, 0x00, MTP_FORMAT_ABSTRACT_AV_PLAYLIST }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_WPL_PLAYLIST, 0x000000000, 0x00, MTP_FORMAT_WPL_PLAYLIST }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_M3U_PLAYLIST, 0x000000000, 0x00, MTP_FORMAT_M3U_PLAYLIST }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_PLS_PLAYLIST, 0x000000000, 0x00, MTP_FORMAT_PLS_PLAYLIST }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_XML_DOCUMENT, 0x000000000, 0x00, MTP_FORMAT_XML_DOCUMENT }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_FLAC, 0x000000000, 0x00, MTP_FORMAT_FLAC }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_AVI, 0x000000000, 0x00, MTP_FORMAT_AVI }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_ASF, 0x000000000, 0x00, MTP_FORMAT_ASF }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_MS_WORD_DOCUMENT, 0x000000000, 0x00, MTP_FORMAT_MS_WORD_DOCUMENT }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_MS_EXCEL_SPREADSHEET, 0x000000000, 0x00, MTP_FORMAT_MS_EXCEL_SPREADSHEET }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16, 0x00, MTP_FORMAT_MS_POWERPOINT_PRESENTATION, 0x000000000, 0x00, MTP_FORMAT_MS_POWERPOINT_PRESENTATION }, + + { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, 0x00, 0x0000000000000000, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, 0x00, 0x00000000, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16, 0x00, 0x0000, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_DISPLAY_NAME, MTP_TYPE_STR, 0x00, 0x0000, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, 0x01, 0x0000, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_DATE_CREATED, MTP_TYPE_STR, 0x00, 0x00, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, 0x00, 0x00, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, 0x00, 0x00000000, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_PERSISTENT_UID, MTP_TYPE_UINT128, 0x00, 0x00, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + { MTP_PROPERTY_NAME, MTP_TYPE_STR, 0x00, 0x00, 0x000000000, 0x00, MTP_FORMAT_ASSOCIATION }, + + { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, 0x00, 0x0000000000000000, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, 0x00, 0x00000000, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16, 0x00, 0x0000, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_DISPLAY_NAME, MTP_TYPE_STR, 0x00, 0x0000, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, 0x01, 0x0000, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_DATE_CREATED, MTP_TYPE_STR, 0x00, 0x00, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, 0x00, 0x00, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, 0x00, 0x00000000, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_PERSISTENT_UID, MTP_TYPE_UINT128, 0x00, 0x00, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + { MTP_PROPERTY_NAME, MTP_TYPE_STR, 0x00, 0x00, 0x000000000, 0x00, MTP_FORMAT_UNDEFINED }, + + //{MTP_PROPERTY_ASSOCIATION_TYPE, MTP_TYPE_UINT16, 0x00, 0x0001 , 0x000000000 , 0x00 , 0xFFFF }, + { MTP_PROPERTY_ASSOCIATION_DESC, MTP_TYPE_UINT32, 0x00, 0x00000000, 0x000000000, 0x00, 0xFFFF }, + { MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16, 0x00, 0x0000, 0x000000000, 0x00, 0xFFFF }, + { MTP_PROPERTY_HIDDEN, MTP_TYPE_UINT16, 0x00, 0x0000, 0x000000000, 0x00, 0xFFFF }, + + { 0xFFFF, MTP_TYPE_UINT32, 0x00, 0x00000000, 0x000000000, 0x00 } +}; + +const profile_property support_device_properties[] = { + // prop_code data_type getset default_value group_code form_flag + //{MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER, MTP_TYPE_UINT32, 0x00, 0x00000000 , 0x000000000 , 0x00 }, + //{MTP_DEVICE_PROPERTY_IMAGE_SIZE, MTP_TYPE_UINT32, 0x00, 0x00000000 , 0x000000000 , 0x00 }, + { MTP_DEVICE_PROPERTY_BATTERY_LEVEL, MTP_TYPE_UINT16, 0x00, 0x00000000, 0x000000000, 0x00 }, + { MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MTP_TYPE_STR, 0x00, 0x00000000, 0x000000000, 0x00 }, + + { 0xFFFF, MTP_TYPE_UINT32, 0x00, 0x00000000, 0x000000000, 0x00 } +}; + +#endif \ No newline at end of file diff --git a/demo/mtp_template.c b/demo/mtp_template.c new file mode 100644 index 00000000..11950d26 --- /dev/null +++ b/demo/mtp_template.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2025 sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include "usbd_core.h" +#include "usbd_mtp.h" + +#if 1 +#error "commercial charge" +#endif + +#ifndef CONFIG_USBDEV_MTP_THREAD +#warning mtp depends on filesystem, suggest to enable CONFIG_USBDEV_MTP_THREAD +#endif + +#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 */ +}; + +__ALIGN_BEGIN const uint8_t WINUSB_IF0_WCIDProperties[142] __ALIGN_END = { + /////////////////////////////////////// + /// WCID property descriptor + /////////////////////////////////////// + 0x8e, 0x00, 0x00, 0x00, /* dwLength */ + 0x00, 0x01, /* bcdVersion */ + 0x05, 0x00, /* wIndex */ + 0x01, 0x00, /* wCount */ + + /////////////////////////////////////// + /// registry propter descriptor + /////////////////////////////////////// + 0x84, 0x00, 0x00, 0x00, /* dwSize */ + 0x01, 0x00, 0x00, 0x00, /* dwPropertyDataType */ + 0x28, 0x00, /* wPropertyNameLength */ + /* DeviceInterfaceGUID */ + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, /* wcName_20 */ + 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, /* wcName_20 */ + 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, /* wcName_20 */ + 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, /* wcName_20 */ + 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, /* wcName_20 */ + 0x4e, 0x00, 0x00, 0x00, /* dwPropertyDataLength */ + + /* {1D4B2365-4749-48EA-B38A-7C6FDDDD7E26} */ + '{', 0x00, '1', 0x00, 'D', 0x00, '4', 0x00, /* wcData_39 */ + 'B', 0x00, '2', 0x00, '3', 0x00, '6', 0x00, /* wcData_39 */ + '5', 0x00, '-', 0x00, '4', 0x00, '7', 0x00, /* wcData_39 */ + '4', 0x00, '9', 0x00, '-', 0x00, '4', 0x00, /* wcData_39 */ + '8', 0x00, 'E', 0x00, 'A', 0x00, '-', 0x00, /* wcData_39 */ + 'B', 0x00, '3', 0x00, '8', 0x00, 'A', 0x00, /* wcData_39 */ + '-', 0x00, '7', 0x00, 'C', 0x00, '6', 0x00, /* wcData_39 */ + 'F', 0x00, 'D', 0x00, 'D', 0x00, 'D', 0x00, /* wcData_39 */ + 'D', 0x00, '7', 0x00, 'E', 0x00, '2', 0x00, /* wcData_39 */ + '6', 0x00, '}', 0x00, 0x00, 0x00, /* wcData_39 */ + +}; + +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 +}; + +/*!< endpoint address */ +#define MTP_IN_EP 0x81 +#define MTP_OUT_EP 0x02 +#define MTP_INT_EP 0x83 + +/*!< config descriptor size */ +#define USB_CONFIG_SIZE (9 + MTP_DESCRIPTOR_LEN) + +static const uint8_t device_descriptor[] = { + USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0201, 0x01) +}; + +static const uint8_t config_descriptor_hs[] = { + USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), + MTP_DESCRIPTOR_INIT(0x00, MTP_OUT_EP, MTP_IN_EP, MTP_INT_EP, USB_BULK_EP_MPS_HS, 0x02), +}; + +static const uint8_t config_descriptor_fs[] = { + USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), + MTP_DESCRIPTOR_INIT(0x00, MTP_OUT_EP, MTP_IN_EP, MTP_INT_EP, USB_BULK_EP_MPS_FS, 0x02), +}; + +static const uint8_t device_quality_descriptor[] = { + USB_DEVICE_QUALIFIER_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, 0x01), +}; + +static const uint8_t other_speed_config_descriptor_hs[] = { + USB_OTHER_SPEED_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), + MTP_DESCRIPTOR_INIT(0x00, MTP_OUT_EP, MTP_IN_EP, MTP_INT_EP, USB_BULK_EP_MPS_FS, 0x02), +}; + +static const uint8_t other_speed_config_descriptor_fs[] = { + USB_OTHER_SPEED_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), + MTP_DESCRIPTOR_INIT(0x00, MTP_OUT_EP, MTP_IN_EP, MTP_INT_EP, USB_BULK_EP_MPS_HS, 0x02), +}; + +static const char *string_descriptors[] = { + (const char[]){ 0x09, 0x04 }, /* Langid */ + "CherryUSB", /* Manufacturer */ + "CherryUSB MTP DEMO", /* Product */ + "2025053000", /* Serial Number */ +}; + +static const uint8_t *device_descriptor_callback(uint8_t speed) +{ + (void)speed; + + return device_descriptor; +} + +static const uint8_t *config_descriptor_callback(uint8_t speed) +{ + if (speed == USB_SPEED_HIGH) { + return config_descriptor_hs; + } else if (speed == USB_SPEED_FULL) { + return config_descriptor_fs; + } else { + return NULL; + } +} + +static const uint8_t *device_quality_descriptor_callback(uint8_t speed) +{ + (void)speed; + + return device_quality_descriptor; +} + +static const uint8_t *other_speed_config_descriptor_callback(uint8_t speed) +{ + if (speed == USB_SPEED_HIGH) { + return other_speed_config_descriptor_hs; + } else if (speed == USB_SPEED_FULL) { + return other_speed_config_descriptor_fs; + } else { + return NULL; + } +} + +static const char *string_descriptor_callback(uint8_t speed, uint8_t index) +{ + (void)speed; + + if (index >= (sizeof(string_descriptors) / sizeof(char *))) { + return NULL; + } + return string_descriptors[index]; +} + +const uint8_t *WINUSB_IFx_WCIDProperties[] = { + WINUSB_IF0_WCIDProperties, +}; + +struct usb_msosv1_descriptor msosv1_desc = { + .string = WCID_StringDescriptor_MSOS, + .vendor_code = WCID_VENDOR_CODE, + .compat_id = WINUSB_WCIDDescriptor, + .comp_id_property = WINUSB_IFx_WCIDProperties, +}; + +const struct usb_bos_descriptor bos_desc = { + .string = bos_descriptor, + .string_len = 22 +}; + +const struct usb_descriptor mtp_descriptor = { + .device_descriptor_callback = device_descriptor_callback, + .config_descriptor_callback = config_descriptor_callback, + .device_quality_descriptor_callback = device_quality_descriptor_callback, + .other_speed_descriptor_callback = other_speed_config_descriptor_callback, + .string_descriptor_callback = string_descriptor_callback, + .bos_descriptor = &bos_desc, + .msosv1_descriptor = &msosv1_desc, +}; + +static void usbd_event_handler(uint8_t busid, 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; + } +} + +static struct usbd_interface intf0; + +extern void usbd_mtp_mount(); + +void mtp_init(uint8_t busid, uint32_t reg_base) +{ + usbd_mtp_mount(); + + usbd_desc_register(busid, &mtp_descriptor); + usbd_add_interface(busid, usbd_mtp_init_intf(&intf0, MTP_OUT_EP, MTP_IN_EP, MTP_INT_EP)); + usbd_initialize(busid, reg_base, usbd_event_handler); +} \ No newline at end of file diff --git a/docs/source/support/img/mtpdev.png b/docs/source/support/img/mtpdev.png new file mode 100644 index 00000000..513b8ac9 Binary files /dev/null and b/docs/source/support/img/mtpdev.png differ diff --git a/docs/source/support/index.rst b/docs/source/support/index.rst index 88f4eb1e..cff81fbf 100644 --- a/docs/source/support/index.rst +++ b/docs/source/support/index.rst @@ -20,7 +20,10 @@ - 主机 UVC & UAC 类 MUSB IP 中 ISO 驱动和 UAC/UVC 框架, MUSB 需要为 mentor 公司制定的标准 IP -- 主从机 MTP 类驱动 +- 从机 MTP 类驱动, 支持多文件和多文件夹 + +.. figure:: img/mtpdev.png + - USB 网卡类高性能版本优化,包含 CDC-NCM, CDC-RNDIS, 私有类驱动(支持多包发送和接收),下面举例 RNDIS .. figure:: img/rndistx.png diff --git a/platform/README.md b/platform/README.md index 639b9efe..79cea2f9 100644 --- a/platform/README.md +++ b/platform/README.md @@ -5,7 +5,8 @@ This is a platform support for other opensource projects. ## Fatfs -Fatfs support with usb host msc. +- Fatfs support with usb host msc. +- Fatfs support with usb device mtp. ## lwip diff --git a/platform/fatfs/usbd_fatfs_mtp.c b/platform/fatfs/usbd_fatfs_mtp.c new file mode 100644 index 00000000..3298560c --- /dev/null +++ b/platform/fatfs/usbd_fatfs_mtp.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2025 sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include "ff.h" +#include "diskio.h" +#include "usbd_core.h" +#include "usbd_mtp.h" + +FATFS s_sd_disk; +FIL s_file; +DIR s_dir; +BYTE work[FF_MAX_SS]; + +const TCHAR driver_num_buf[4] = { '0', ':', '/', '\0' }; + +const char *show_error_string(FRESULT fresult); + +static FRESULT sd_mount_fs(void) +{ + FRESULT fresult = f_mount(&s_sd_disk, driver_num_buf, 1); + if (fresult == FR_OK) { + printf("SD card has been mounted successfully\n"); + } else { + printf("Failed to mount SD card, cause: %s\n", show_error_string(fresult)); + } + + return fresult; +} + +#if 0 +static FRESULT sd_mkfs(void) +{ + printf("Formatting the SD card, depending on the SD card capacity, the formatting process may take a long time\n"); + FRESULT fresult = f_mkfs(driver_num_buf, NULL, work, sizeof(work)); + if (fresult != FR_OK) { + printf("Making File system failed, cause: %s\n", show_error_string(fresult)); + } else { + printf("Making file system is successful\n"); + } + + return fresult; +} +#endif + +static FRESULT sd_write_file(void) +{ + FRESULT fresult = f_open(&s_file, "0:/readme.txt", FA_WRITE | FA_CREATE_ALWAYS); + if (fresult != FR_OK) { + printf("Create new file failed, cause: %d\n", show_error_string(fresult)); + } else { + printf("Create new file successfully, status=%d\n", fresult); + } + char hello_str[] = "Hello, this is SD card FATFS demo\n"; + UINT byte_written; + fresult = f_write(&s_file, hello_str, sizeof(hello_str), &byte_written); + if (fresult != FR_OK) { + printf("Write file failed, cause: %s\n", show_error_string(fresult)); + } else { + printf("Write file operation is successfully\n"); + } + + f_close(&s_file); + + return fresult; +} + +const char *show_error_string(FRESULT fresult) +{ + const char *result_str; + + switch (fresult) { + case FR_OK: + result_str = "succeeded"; + break; + case FR_DISK_ERR: + result_str = "A hard error occurred in the low level disk I/O level"; + break; + case FR_INT_ERR: + result_str = "Assertion failed"; + break; + case FR_NOT_READY: + result_str = "The physical drive cannot work"; + break; + case FR_NO_FILE: + result_str = "Could not find the file"; + break; + case FR_NO_PATH: + result_str = "Could not find the path"; + break; + case FR_INVALID_NAME: + result_str = "Tha path name format is invalid"; + break; + case FR_DENIED: + result_str = "Access denied due to prohibited access or directory full"; + break; + case FR_EXIST: + result_str = "Access denied due to prohibited access"; + break; + case FR_INVALID_OBJECT: + result_str = "The file/directory object is invalid"; + break; + case FR_WRITE_PROTECTED: + result_str = "The physical drive is write protected"; + break; + case FR_INVALID_DRIVE: + result_str = "The logical driver number is invalid"; + break; + case FR_NOT_ENABLED: + result_str = "The volume has no work area"; + break; + case FR_NO_FILESYSTEM: + result_str = "There is no valid FAT volume"; + break; + case FR_MKFS_ABORTED: + result_str = "THe f_mkfs() aborted due to any problem"; + break; + case FR_TIMEOUT: + result_str = "Could not get a grant to access the volume within defined period"; + break; + case FR_LOCKED: + result_str = "The operation is rejected according to the file sharing policy"; + break; + case FR_NOT_ENOUGH_CORE: + result_str = "LFN working buffer could not be allocated"; + break; + case FR_TOO_MANY_OPEN_FILES: + result_str = "Number of open files > FF_FS_LOCK"; + break; + case FR_INVALID_PARAMETER: + result_str = "Given parameter is invalid"; + break; + default: + result_str = "Unknown error"; + break; + } + return result_str; +} + +const char *usbd_mtp_fs_root_path(void) +{ + return driver_num_buf; +} + +const char *usbd_mtp_fs_description(void) +{ + return "CherryUSB MTP"; +} + +int usbd_mtp_mkdir(const char *path) +{ + FRESULT result = f_mkdir(path); + if (result != FR_OK) { + printf("f_mkdir failed, cause: %s\n", show_error_string(result)); + return -1; + } + return 0; // Directory created successfully +} + +int usbd_mtp_rmdir(const char *path) +{ + FRESULT result = f_rmdir(path); + if (result != FR_OK) { + printf("f_mkdir failed, cause: %s\n", show_error_string(result)); + return -1; + } + return 0; // Directory created successfully +} + +MTP_DIR *usbd_mtp_opendir(const char *name) +{ + f_opendir(&s_dir, name); + return (MTP_DIR *)&s_dir; +} + +int usbd_mtp_closedir(MTP_DIR *dir) +{ + return f_closedir((DIR *)dir); +} + +struct mtp_dirent *usbd_mtp_readdir(MTP_DIR *dir) +{ + FILINFO fno; + FRESULT result; + + result = f_readdir((DIR *)dir, &fno); + if (result != FR_OK || fno.fname[0] == 0) + return NULL; + + static struct mtp_dirent dirent; + memset(&dirent, 0, sizeof(struct mtp_dirent)); + strncpy(dirent.d_name, fno.fname, sizeof(dirent.d_name) - 1); + dirent.d_name[sizeof(dirent.d_name) - 1] = '\0'; + dirent.d_namlen = strlen(dirent.d_name); + + return &dirent; +} + +#undef SS +#if FF_MAX_SS == FF_MIN_SS +#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + +int usbd_mtp_stat(const char *path, struct stat *buf) +{ + FILINFO file_info; + FRESULT result; + FATFS *f; + f = &s_sd_disk; + + result = f_stat(path, &file_info); + if (result != FR_OK) { + printf("f_stat failed, cause: %s\n", show_error_string(result)); + return -1; + } + buf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH; + if (file_info.fattrib & AM_DIR) { + buf->st_mode &= ~S_IFREG; + buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + } + if (file_info.fattrib & AM_RDO) + buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + + buf->st_size = file_info.fsize; + buf->st_blksize = f->csize * SS(f); + if (file_info.fattrib & AM_ARC) { + buf->st_blocks = file_info.fsize ? ((file_info.fsize - 1) / SS(f) / f->csize + 1) : 0; + buf->st_blocks *= (buf->st_blksize / 512); // man say st_blocks is number of 512B blocks allocated + } else { + buf->st_blocks = f->csize; + } + return 0; +} + +int usbd_mtp_statfs(const char *path, struct mtp_statfs *buf) +{ + FATFS *f; + FRESULT res; + DWORD fre_clust, fre_sect, tot_sect; + + f = &s_sd_disk; + + res = f_getfree(path, &fre_clust, &f); + if (res != FR_OK) { + printf("f_getfree failed, cause: %s\n", show_error_string(res)); + return -1; + } + tot_sect = (f->n_fatent - 2) * f->csize; + fre_sect = fre_clust * f->csize; + + buf->f_blocks = tot_sect; + buf->f_bfree = fre_sect; +#if FF_MAX_SS != FF_MIN_SS + buf->f_bsize = f->ssize; +#else + buf->f_bsize = FF_MIN_SS; +#endif + return 0; +} + +int usbd_mtp_open(const char *path, uint8_t mode) +{ + BYTE flags; + + if (mode == O_RDONLY) { + flags = FA_READ | FA_OPEN_EXISTING; + } else if (mode == O_WRONLY) { + flags = FA_WRITE | FA_OPEN_ALWAYS; + } else if (mode == O_RDWR) { + flags = FA_READ | FA_WRITE | FA_OPEN_ALWAYS; + } else { + return -1; // Invalid mode + } + + FRESULT result = f_open(&s_file, path, flags); + if (result != FR_OK) { + printf("f_open failed, cause: %s\n", show_error_string(result)); + return -1; + } + return 0; +} + +int usbd_mtp_close(int fd) +{ + FRESULT result = f_close(&s_file); + if (result != FR_OK) { + printf("f_close failed, cause: %s\n", show_error_string(result)); + return -1; + } + return 0; +} + +int usbd_mtp_read(int fd, void *buf, size_t len) +{ + UINT bytes_read; + FRESULT result = f_read(&s_file, buf, len, &bytes_read); + if (result != FR_OK) { + printf("f_read failed, cause: %s\n", show_error_string(result)); + return -1; + } + return bytes_read; // Return number of bytes read +} + +int usbd_mtp_write(int fd, const void *buf, size_t len) +{ + UINT bytes_written; + FRESULT result = f_write(&s_file, buf, len, &bytes_written); + if (result != FR_OK) { + printf("f_write failed, cause: %s\n", show_error_string(result)); + return -1; + } + return bytes_written; // Return number of bytes written +} + +int usbd_mtp_unlink(const char *path) +{ + FRESULT result = f_unlink(path); + if (result != FR_OK) { + printf("f_unlink failed, cause: %s\n", show_error_string(result)); + return -1; + } + return 0; // File deleted successfully +} + +void usbd_mtp_mount() +{ + sd_mount_fs(); + + // write a file to test the SD card + sd_write_file(); +}