2026-04-28 7:00 AM
Environment:
Board: STM32H7S78-DK
Storage: microSD Card.
Host OS: Windows 11
Preliminary Note:
A significant portion of this code is adapted from the official ST example:
Current Status:
I have successfully implemented the MTP class. The device is correctly recognized by Windows, the storage is mounted, and I can view and modify files located in the root directory.
To manage MTP objects, I use a custom function SD_Scan, which traverses the FileX filesystem and builds a local object handle table (ObjectHandleTypeDef).
VOID SD_Scan(VOID)
{
/* Estructura estática para no usar el STACK */
static FolderList_t folder_list[SD_MAX_HANDLES];
/* Variables de control de la cola con nombres claros */
UINT NextFolderToScan = 0;
UINT TotalFoldersInList = 0;
/* Variables de estado y buffers (Declaradas arriba por limpieza) */
UINT status;
CHAR file_name[FX_MAX_LONG_NAME_LEN];
UINT attributes;
ULONG size;
/* Variables temporales para la carpeta que estamos procesando ahora */
CHAR *current_path;
ULONG current_parent;
ULONG new_folder_handle;
/* 1. Empezamos añadiendo la raíz a la cola */
strcpy(folder_list[TotalFoldersInList].path, "/");
folder_list[TotalFoldersInList].parent_handle = 0;
TotalFoldersInList++;
/* 2. Bucle principal: mientras queden carpetas por explorar */
while (NextFolderToScan < TotalFoldersInList)
{
/* Sacamos los datos de la carpeta que toca escanear ahora */
current_path = folder_list[NextFolderToScan].path;
current_parent = folder_list[NextFolderToScan].parent_handle;
NextFolderToScan++;
/* Informamos a FileX de que queremos trabajar en esta carpeta */
fx_directory_default_set(&sdmcc_sd1, current_path);
/* Buscamos el primer elemento */
status = fx_directory_first_full_entry_find(&sdmcc_sd1, file_name, &attributes, &size,
FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL);
while (status == FX_SUCCESS)
{
/* Ignorar las entradas "." y ".." */
if (file_name[0] != '.')
{
if (attributes & FX_DIRECTORY)
{
/* --- ENCONTRAMOS UNA SUB-CARPETA --- */
SD_object_handles_counter++;
/* La registramos en nuestra tabla global de objetos MTP */
SD_Object_SetHandleInfo(&SD_object_handle_index_num, file_name, 0, current_parent, FORMAT_DIRECTORY);
/* Guardamos el Handle que acaba de recibir esta carpeta para sus futuros hijos */
new_folder_handle = SD_object_handle_index_num;
/* Si hay hueco en la cola, la guardamos para explorarla luego */
if (TotalFoldersInList < SD_MAX_HANDLES)
{
/* Construimos la ruta absoluta (Path) */
if (strcmp(current_path, "/") == 0)
sprintf(folder_list[TotalFoldersInList].path, "/%s", file_name);
else
sprintf(folder_list[TotalFoldersInList].path, "%s/%s", current_path, file_name);
/* El padre de lo que hay dentro de esta carpeta es el Handle que acabamos de crear */
folder_list[TotalFoldersInList].parent_handle = new_folder_handle;
TotalFoldersInList++;
}
}
else
{
/* --- ENCONTRAMOS UN ARCHIVO --- */
SD_object_handles_counter++;
ULONG format = SD_Object_GetFormatFromName(file_name);
SD_Object_SetHandleInfo(&SD_object_handle_index_num, file_name, size, current_parent, format);
}
}
/* Siguiente entrada en la carpeta actual */
status = fx_directory_next_full_entry_find(&sdmcc_sd1, file_name, &attributes, &size,
FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL);
}
}
}For simplicity, I am currently working only with:
I have debugged this function, and its behavior is as expected, so I do not believe it is the source of the issue.
I am also aware that the implementation of USBD_MTP_SendObjectInfo is currently incomplete. However, my immediate priority is to correctly display the file and folder hierarchy in the host. Until this is working properly, I do not plan to proceed further with additional MTP features.
Problem:
In the original ST example, the following function is used to return all object handles to the host:
/**
* @brief Object_GetHandlesIndex
* Get all object handle.
* @PAram Param3: current object handle
* @PAram obj_handle: all objects handle files in current object
* @retval number of object handle in current object
*/
ULONG Object_GetHandlesIndex(ULONG Param3, ULONG *obj_handle)
{
UINT index;
ULONG object_handle_num = 0;
if (Param3 == 0xFFFFFFFF)
{
for (index = 0; index < MTP_MAX_HANDLES; index++)
{
if (ObjectHandleInfo[index].object_handle_index != 0)
{
obj_handle[index] = ObjectHandleInfo[index].object_handle_index;
}
}
/* Return number of object in Root folder */
object_handle_num = object_handles_counter;
}
return object_handle_num;
}This implementation returns all objects when Param3 == 0xFFFFFFFF (root).
However, the issue is that Windows displays all files as if they were located in the root directory, ignoring the folder hierarchy.
Attempted Solution:
To address this, I modified the function as follows:
/**
* @brief SD_Object_GetHandlesIndex
* Get all object handle.
* @PAram Param3: current object handle
* @PAram obj_handle: all objects handle files in current object
* @retval number of object handle in current object
*/
ULONG SD_Object_GetHandlesIndex(ULONG Param3, ULONG *obj_handle)
{
UINT index;
ULONG object_handle_num = 0;
/* MTP usa 0 o 0xFFFFFFFF para referirse a la raíz (Root) */
ULONG target_parent = (Param3 == 0xFFFFFFFF) ? 0 : Param3;
for (index = 0; index < SD_MAX_HANDLES; index++)
{
/* Filtro: El objeto debe existir Y su padre debe ser el solicitado por el PC */
if (SD_ObjectHandleInfo[index].object_handle_index != 0 &&
SD_ObjectHandleInfo[index].object_property.object_parent_object == target_parent)
{
obj_handle[object_handle_num] = SD_ObjectHandleInfo[index].object_handle_index;
object_handle_num++;
}
}
return object_handle_num;
}The idea is to:
I also take into account that MTP uses:
I implemented this approach based on the TinyUSB MTP example, where object handles are also filtered by parent to represent the directory hierarchy.
Although TinyUSB is a different middleware, both implementations rely on the same MTP protocol. Therefore, the underlying logic for handling object hierarchy should, in principle, be the same.
Initial Assumption (Incorrect):
I initially assumed that when navigating into a folder from the Windows Explorer:
USBD_MTP_GetObjectHandles would be called again
The host would request the handles corresponding to that specific folder
However, based on my observations, this is not how it actually works.
How should GetObjectHandles be properly implemented in MTP so that Windows correctly displays a hierarchical folder structure?
I tried to read the Media Transfer Protocol v1.1, but I couldn’t find anything clearly useful for my case, probably due to my inexperience with this topic.
I will attach the full project to the post, but I’ll also include the modifications I’ve made to the ST example. I’ll try to list all of them, though I might miss some.
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define USBD_MTP_SUPPORTED_PROP \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_STORAGEID, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_FORMAT, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PROTECTION_STATUS, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_SIZE, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_FILE_NAME, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PARENT_OBJECT, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_NAME
#define USBD_MTP_SUPPORTED_ASSOC_PROP \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_STORAGEID, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_FORMAT, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PROTECTION_STATUS, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_SIZE, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ASSOCIATION_TYPE, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ASSOCIATION_DESC, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_FILE_NAME, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PARENT_OBJECT, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER, \
UX_DEVICE_CLASS_PIMA_OBJECT_PROP_NAME
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* Define PIMA supported device properties */
USHORT USBD_MTP_DevicePropSupported[] = {
/* USER CODE BEGIN USBD_MTP_DevicePropSupported */
UX_DEVICE_CLASS_PIMA_DEV_PROP_DEVICE_FRIENDLY_NAME,
/* USER CODE END USBD_MTP_DevicePropSupported */
0
};
/* Define PIMA supported capture formats */
USHORT USBD_MTP_DeviceSupportedCaptureFormats[] = {
/* USER CODE BEGIN USBD_MTP_DeviceSupportedCaptureFormats */
UX_DEVICE_CLASS_PIMA_OFC_ASSOCIATION,
UX_DEVICE_CLASS_PIMA_OFC_TEXT,
/* USER CODE END USBD_MTP_DeviceSupportedCaptureFormats */
0
};
/* Define PIMA supported image formats */
USHORT USBD_MTP_DeviceSupportedImageFormats[] = {
/* USER CODE BEGIN USBD_MTP_DeviceSupportedImageFormats */
UX_DEVICE_CLASS_PIMA_OFC_ASSOCIATION,
UX_DEVICE_CLASS_PIMA_OFC_TEXT,
/* USER CODE END USBD_MTP_DeviceSupportedImageFormats */
0
};
/* Object property supported
WORD 0 : Object Format Code
WORD 1 : Number of Prop codes for this Object format
WORD n : Prop Codes
WORD n+2 : Next Object Format code ....
*/
USHORT USBD_MTP_ObjectPropSupported[] = {
/* USER CODE BEGIN USBD_MTP_ObjectPropSupported */
/* Object format code : Text */
UX_DEVICE_CLASS_PIMA_OFC_TEXT,
/* NUmber of objects supported for this format */
8,
/* Mandatory objects for all formats */
USBD_MTP_SUPPORTED_PROP,
/* Object format code : Text */
UX_DEVICE_CLASS_PIMA_OFC_ASSOCIATION,
/* NUmber of objects supported for this format */
10,
/* Mandatory objects for all formats */
USBD_MTP_SUPPORTED_ASSOC_PROP,
};
/**
* @brief USBD_MTP_SetObjectPropValue
* This function is invoked when host requested to set object
* prop value.
* @PAram pima_instance : Pointer to the pima class instance.
* @PAram object_handle : Handle of the object.
* @PAram object_prop_code : Object property code.
* @PAram object_prop_value: Object property value.
* @PAram object_prop_value_length: Object property value length.
* @retval status
*/
UINT USBD_MTP_SetObjectPropValue(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima_instance,
ULONG object_handle,
ULONG object_prop_code,
UCHAR *object_prop_value,
ULONG object_prop_value_length)
{
UINT status = UX_SUCCESS;
/* USER CODE BEGIN USBD_MTP_SetObjectPropValue */
UX_PARAMETER_NOT_USED(pima_instance);
UX_PARAMETER_NOT_USED(object_prop_value_length);
ULONG handle_index;
ObjectPropertyDataSetTypeDef *object_info;
/* Check the object handle exist */
status = SD_Object_HandleCheck(object_handle, &handle_index);
if (status == UX_SUCCESS)
{
/* Get object Property info */
SD_Object_GetHandleInfo(handle_index, (VOID**) &object_info);
/* switch object property code */
switch (object_prop_code)
{
case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_FILE_NAME :
/* Copy the file name after translate from Unicode to ASCIIZ */
ux_utility_unicode_to_string(object_prop_value, object_info->object_file_full_name);
status = UX_SUCCESS;
break;
case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_NAME :
/* Copy the name after translate from Unicode to ASCIIZ */
ux_utility_unicode_to_string(object_prop_value, object_info->object_file_name);
status = UX_SUCCESS;
break;
case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_STORAGEID :
case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_FORMAT :
case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_SIZE :
case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ASSOCIATION_TYPE :
case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ASSOCIATION_DESC :
case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PARENT_OBJECT :
/* Object is write protected */
status = UX_DEVICE_CLASS_PIMA_RC_OBJECT_WRITE_PROTECTED;
break;
default :
status = UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_PROP_CODE;
break;
}
}
else
{
/* Invalid object handle */
status = UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_HANDLE;
}
/* USER CODE END USBD_MTP_SetObjectPropValue */
return status;
}
/**
* @brief MTP_GetObjectHandle
* This function is called to get pima mtp object struct.
* @PAram object_handle: object handle.
* @PAram object: pima mtp object struct.
* @retval status
*/
UINT MTP_GetObjectHandle(ULONG object_handle, UX_SLAVE_CLASS_PIMA_OBJECT **object)
{
UINT status, index;
for (index = 0; index < SD_object_handles_counter; index++)
{
if (SD_ObjectHandleInfo[index].object_handle_index == object_handle)
{
ux_utility_memory_set(&MTP_Object, 0, sizeof(MTP_Object));
/* Fill MTP object struct */
MTP_Object.ux_device_class_pima_object_storage_id = SD_ObjectHandleInfo[index].object_property.object_storage_id;
MTP_Object.ux_device_class_pima_object_format = SD_ObjectHandleInfo[index].object_property.object_format;
MTP_Object.ux_device_class_pima_object_compressed_size = SD_ObjectHandleInfo[index].object_property.object_size;
MTP_Object.ux_device_class_pima_object_protection_status = SD_ObjectHandleInfo[index].object_property.object_protection_status;
MTP_Object.ux_device_class_pima_object_thumb_format = UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED;
MTP_Object.ux_device_class_pima_object_thumb_compressed_size = 0U;
MTP_Object.ux_device_class_pima_object_thumb_pix_height = 0U;
MTP_Object.ux_device_class_pima_object_thumb_pix_width = 0U;
MTP_Object.ux_device_class_pima_object_image_pix_height = 0U;
MTP_Object.ux_device_class_pima_object_image_pix_width = 0U;
MTP_Object.ux_device_class_pima_object_image_bit_depth = 0U;
MTP_Object.ux_device_class_pima_object_parent_object = SD_ObjectHandleInfo[index].object_property.object_parent_object;
if (MTP_Object.ux_device_class_pima_object_format == UX_DEVICE_CLASS_PIMA_OFC_ASSOCIATION)
{
/* Generic folder association type (MTP/PTP value 0x0001). */
MTP_Object.ux_device_class_pima_object_association_type = 1U;
}
else
{
MTP_Object.ux_device_class_pima_object_association_type = 0U;
}
MTP_Object.ux_device_class_pima_object_association_desc = 0U;
MTP_Object.ux_device_class_pima_object_sequence_number = 0U;
ux_utility_string_to_unicode(SD_ObjectHandleInfo[index].object_property.object_file_full_name,
MTP_Object.ux_device_class_pima_object_filename);
*object = &MTP_Object;
status = UX_SUCCESS;
}
}
return status;
}The SD card scanning works correctly, and the object table appears consistent.
The issue lies in how object handles are managed and returned to the host, particularly regarding hierarchy handling.
I would appreciate guidance from someone with more experience in MTP or USBX regarding:
Solved! Go to Solution.
2026-05-08 2:35 AM
The problem occurs because there is a bug in the ST example, which contains an error causing the StorageID property description to be incorrect.
In the USBD_MTP_GetObjectPropDesc function, in the case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_STORAGEID:
/* Fill MTP object property struct */
MTP_ObjectPropDesc.ObjectPropertyCode = UX_DEVICE_CLASS_PIMA_OBJECT_PROP_STORAGEID;
MTP_ObjectPropDesc.DataType = UX_DEVICE_CLASS_PIMA_TYPES_UINT16;
MTP_ObjectPropDesc.GetSet = UX_DEVICE_CLASS_PIMA_OBJECT_PROPERTY_DATASET_VALUE_GET;
MTP_ObjectPropDesc.DefValue = (uint8_t *)&storageid;
MTP_ObjectPropDesc.GroupCode = 0U;
MTP_ObjectPropDesc.FormFlag = 0U;It is necessary to change:
MTP_ObjectPropDesc.DataType = UX_DEVICE_CLASS_PIMA_TYPES_UINT16;to:
MTP_ObjectPropDesc.DataType = UX_DEVICE_CLASS_PIMA_TYPES_UINT32;This was causing MTP to not recognize my storage correctly, and therefore it could not detect that the storage had a hierarchical structure.
The correct way to describe this parameter is specified in the Media Transfer Protocol Specification Revision 1.1 (April 6, 2011), Appendix B, section B2.1.
2026-05-04 7:58 AM
I've been looking into the code and found the following definition in the ux_device_pima_mtp.h file:
#ifndef USBD_MTP_STORAGE_FILE_SYSTEM_TYPE
#define USBD_MTP_STORAGE_FILE_SYSTEM_TYPE UX_DEVICE_CLASS_PIMA_FSTC_GENERIC_FLAT
#endifI suspected this was the main cause of the issue, so I modified this value inside the USBD_MTP_GetStorageInfo function in the ux_device_pima_mtp.c file. However, even after making this change, I still can't get the file explorer to display the folder structure correctly.
/**
* @brief USBD_MTP_GetStorageInfo
* This function is invoked when host requested to get storage
* media information.
* @PAram pima_instance: Pointer to the pima class instance.
* @PAram storage_id: Storage id.
* @retval status
*/
UINT USBD_MTP_GetStorageInfo(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima_instance,
ULONG storage_id)
{
UINT status = UX_SUCCESS;
/* USER CODE BEGIN USBD_MTP_GetStorageInfo */
/* Check the media storage ID */
if (storage_id == USBD_MTP_STORAGE_ID)
{
/* Fill the mtp pima storage information */
pima_instance->ux_device_class_pima_storage_type = USBD_MTP_STORAGE_TYPE;
pima_instance->ux_device_class_pima_storage_file_system_type = UX_DEVICE_CLASS_PIMA_FSTC_GENERIC_HIERARCHICAL;
pima_instance->ux_device_class_pima_storage_access_capability = USBD_MTP_STORAGE_FILE_ACCESS_CAPABILITY;
pima_instance->ux_device_class_pima_storage_max_capacity_low = USBD_MTP_StorageGetMaxCapabilityLow();
pima_instance->ux_device_class_pima_storage_max_capacity_high = USBD_MTP_StorageGetMaxCapabilityHigh();
pima_instance->ux_device_class_pima_storage_free_space_low = USBD_MTP_StorageGetFreeSpaceLow();
pima_instance->ux_device_class_pima_storage_free_space_high = USBD_MTP_StorageGetFreeSpaceHigh();
pima_instance->ux_device_class_pima_storage_free_space_image = USBD_MTP_StorageGetFreeSpaceImage();
pima_instance->ux_device_class_pima_storage_description = (UCHAR*)USBD_MTP_STORAGE_DESCRIPTION;
pima_instance->ux_device_class_pima_storage_volume_label = (UCHAR*)USBD_MTP_STORAGE_DESCRIPTION_IDENTIFIER;
status = UX_SUCCESS;
}
else
{
/* Invalid storage ID */
status = UX_DEVICE_CLASS_PIMA_RC_INVALID_STORAGE_ID;
}
/* USER CODE END USBD_MTP_GetStorageInfo */
return status;
}
2026-05-05 11:02 PM
Hi @jasonjames2 @, thanks for your reply. I’ve checked what you mentioned, although I don’t understand where the link you sent leads, and I don’t think it’s the source of the problem. I’ve done some debugging and the hierarchy looks correct. I’ve also reviewed the commands sent by the PC and can confirm that at no point are the handlers requested when I navigate into the folders. I’m sharing the commands the PC sends me in case someone can spot any clues about what might be happening
[000] TR:0000 CMD:0x1002 P1:0x00000001 P2:0x00000000 P3:0x00000000
[001] TR:0001 CMD:0x1001 P1:0x00000001 P2:0x00000000 P3:0x00000000
[002] TR:0002 CMD:0x1014 P1:0x0000D402 P2:0x00000000 P3:0x00000000
[003] TR:0003 CMD:0x1004 P1:0x0000D402 P2:0x00000000 P3:0x00000000
[004] TR:0004 CMD:0x1005 P1:0x00010001 P2:0x00000000 P3:0x00000000
[005] TR:0005 CMD:0x9801 P1:0x00003001 P2:0x00000000 P3:0x00000000
[006] TR:0006 CMD:0x9801 P1:0x00003004 P2:0x00000000 P3:0x00000000
[007] TR:0007 CMD:0x1007 P1:0x00010001 P2:0x00000000 P3:0xFFFFFFFF
[008] TR:0008 CMD:0x9803 P1:0x00000001 P2:0x0000DC02 P3:0xFFFFFFFF
[009] TR:0009 CMD:0x9802 P1:0x0000DC03 P2:0x00003001 P3:0xFFFFFFFF
[010] TR:0010 CMD:0x9803 P1:0x00000001 P2:0x0000DC03 P3:0xFFFFFFFF
[011] TR:0011 CMD:0x9802 P1:0x0000DC04 P2:0x00003001 P3:0xFFFFFFFF
[012] TR:0012 CMD:0x9803 P1:0x00000001 P2:0x0000DC04 P3:0xFFFFFFFF
[013] TR:0013 CMD:0x1008 P1:0x00000001 P2:0x0000DC04 P3:0xFFFFFFFF
[014] TR:0014 CMD:0x9802 P1:0x0000DC01 P2:0x00003001 P3:0xFFFFFFFF
[015] TR:0015 CMD:0x9803 P1:0x00000001 P2:0x0000DC01 P3:0xFFFFFFFF
[016] TR:0016 CMD:0x9802 P1:0x0000DC05 P2:0x00003001 P3:0xFFFFFFFF
[017] TR:0017 CMD:0x9803 P1:0x00000001 P2:0x0000DC05 P3:0xFFFFFFFF
[018] TR:0018 CMD:0x9802 P1:0x0000DC06 P2:0x00003001 P3:0xFFFFFFFF
[019] TR:0019 CMD:0x9803 P1:0x00000001 P2:0x0000DC06 P3:0xFFFFFFFF
[020] TR:0020 CMD:0x9802 P1:0x0000DC07 P2:0x00003001 P3:0xFFFFFFFF
[021] TR:0021 CMD:0x9803 P1:0x00000001 P2:0x0000DC07 P3:0xFFFFFFFF
[022] TR:0022 CMD:0x9802 P1:0x0000DC41 P2:0x00003001 P3:0xFFFFFFFF
[023] TR:0023 CMD:0x9803 P1:0x00000001 P2:0x0000DC41 P3:0xFFFFFFFF
[024] TR:0024 CMD:0x9802 P1:0x0000DC44 P2:0x00003001 P3:0xFFFFFFFF
[025] TR:0025 CMD:0x9803 P1:0x00000001 P2:0x0000DC44 P3:0xFFFFFFFF
[026] TR:0026 CMD:0x9810 P1:0x00000001 P2:0x0000DC44 P3:0xFFFFFFFF
[027] TR:0027 CMD:0x9803 P1:0x00000002 P2:0x0000DC02 P3:0xFFFFFFFF
[028] TR:0028 CMD:0x9803 P1:0x00000002 P2:0x0000DC03 P3:0xFFFFFFFF
[029] TR:0029 CMD:0x9803 P1:0x00000002 P2:0x0000DC04 P3:0xFFFFFFFF
[030] TR:0030 CMD:0x1008 P1:0x00000002 P2:0x0000DC04 P3:0xFFFFFFFF
[031] TR:0031 CMD:0x9803 P1:0x00000002 P2:0x0000DC01 P3:0xFFFFFFFF
[032] TR:0032 CMD:0x9803 P1:0x00000002 P2:0x0000DC05 P3:0xFFFFFFFF
[033] TR:0033 CMD:0x9803 P1:0x00000002 P2:0x0000DC06 P3:0xFFFFFFFF
[034] TR:0034 CMD:0x9803 P1:0x00000002 P2:0x0000DC07 P3:0xFFFFFFFF
[035] TR:0035 CMD:0x9803 P1:0x00000002 P2:0x0000DC41 P3:0xFFFFFFFF
[036] TR:0036 CMD:0x9803 P1:0x00000002 P2:0x0000DC44 P3:0xFFFFFFFF
[037] TR:0037 CMD:0x9810 P1:0x00000002 P2:0x0000DC44 P3:0xFFFFFFFF
2026-05-08 2:35 AM
The problem occurs because there is a bug in the ST example, which contains an error causing the StorageID property description to be incorrect.
In the USBD_MTP_GetObjectPropDesc function, in the case UX_DEVICE_CLASS_PIMA_OBJECT_PROP_STORAGEID:
/* Fill MTP object property struct */
MTP_ObjectPropDesc.ObjectPropertyCode = UX_DEVICE_CLASS_PIMA_OBJECT_PROP_STORAGEID;
MTP_ObjectPropDesc.DataType = UX_DEVICE_CLASS_PIMA_TYPES_UINT16;
MTP_ObjectPropDesc.GetSet = UX_DEVICE_CLASS_PIMA_OBJECT_PROPERTY_DATASET_VALUE_GET;
MTP_ObjectPropDesc.DefValue = (uint8_t *)&storageid;
MTP_ObjectPropDesc.GroupCode = 0U;
MTP_ObjectPropDesc.FormFlag = 0U;It is necessary to change:
MTP_ObjectPropDesc.DataType = UX_DEVICE_CLASS_PIMA_TYPES_UINT16;to:
MTP_ObjectPropDesc.DataType = UX_DEVICE_CLASS_PIMA_TYPES_UINT32;This was causing MTP to not recognize my storage correctly, and therefore it could not detect that the storage had a hierarchical structure.
The correct way to describe this parameter is specified in the Media Transfer Protocol Specification Revision 1.1 (April 6, 2011), Appendix B, section B2.1.