Skip to content

usb: device_next: Add USB MTP class support #86832

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

ExtremeGTX
Copy link
Collaborator

@ExtremeGTX ExtremeGTX commented Mar 10, 2025

Implement a basic version of USB MTP (Media transfer protocol) class which support
the necessary MTP commands to handle Dir/file transfer between a device and host.
Fixes: #54468

TODO:

  • Address FIXME comment(s): mainly handle cancel transaction request.
  • Test on USB Full speed (rpi_pico)
  • Test on another board than STM32F769i-DISCO
    • rpi_pico 2040
  • Remove Extensive logging
  • Make MAX_PACKET_SIZE value dependent on whether USB is HS/FS
  • Polishing the MTP Sample including updating the readme file
  • Improve Error handling

Thanks:

  • Zephyr USB Team for helping me understand how device_next works
  • HHD Software for supporting this project.

@ExtremeGTX
Copy link
Collaborator Author

Dear Reviewers:

Could you please help me validate the current architecture first, then we can move to final polishing when I get the PR out of draft state.

Thanks a lot.

@ExtremeGTX ExtremeGTX force-pushed the usb_mtp_next_PR branch 2 times, most recently from 7874f45 to 748eb7e Compare March 10, 2025 03:37
@ExtremeGTX ExtremeGTX requested a review from kartben March 14, 2025 20:11
@ExtremeGTX ExtremeGTX force-pushed the usb_mtp_next_PR branch 2 times, most recently from f6bfa5e to 02a2fb9 Compare April 21, 2025 21:03
@ExtremeGTX ExtremeGTX requested a review from jfischer-no April 21, 2025 21:06
@tmon-nordic
Copy link
Collaborator

The class fixup/polishing commits should be squashed together into the main MTP class implementation commit. Sample changes should go into separate commits.

@kartben kartben requested a review from Copilot April 24, 2025 01:01
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements basic USB MTP class support to enable file and directory transfers between a device and a host. Key changes include:

  • Adding a new MTP class header with necessary function declarations and context structure.
  • Providing a sample application to test and demonstrate the MTP functionality.
  • Updating USB descriptors, DTS bindings, and sample configuration to support MTP.

Reviewed Changes

Copilot reviewed 7 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
subsys/usb/device_next/class/usbd_mtp_class.h Introduces declarations for the USB MTP class support
samples/subsys/usb/mtp/src/main.c Implements a sample application to test the MTP class driver
samples/subsys/usb/mtp/sample.yaml Adds a sample configuration for the MTP device class
include/zephyr/usb/usb_ch9.h Updates USB descriptors to include the MTP image class type
dts/bindings/fs/zephyr,fstab-common.yaml Adds a filesystem binding property for enabling MTP file access
Files not reviewed (11)
  • samples/subsys/usb/mtp/CMakeLists.txt: Language not supported
  • samples/subsys/usb/mtp/Kconfig: Language not supported
  • samples/subsys/usb/mtp/README.rst: Language not supported
  • samples/subsys/usb/mtp/boards/rpi_pico.overlay: Language not supported
  • samples/subsys/usb/mtp/boards/stm32f769i_disco.overlay: Language not supported
  • samples/subsys/usb/mtp/prj.conf: Language not supported
  • subsys/usb/device_next/CMakeLists.txt: Language not supported
  • subsys/usb/device_next/class/Kconfig: Language not supported
  • subsys/usb/device_next/class/Kconfig.mtp: Language not supported
  • tests/subsys/usb/device_next/build_all.conf: Language not supported
  • tests/subsys/usb/device_next/build_all.overlay: Language not supported

@jfischer-no jfischer-no removed the request for review from kartben April 24, 2025 08:39
@jfischer-no jfischer-no self-assigned this Apr 24, 2025
@kartben kartben self-requested a review April 24, 2025 20:34
@ExtremeGTX ExtremeGTX force-pushed the usb_mtp_next_PR branch 2 times, most recently from 3bb7378 to 9910433 Compare April 24, 2025 21:51
@ExtremeGTX ExtremeGTX marked this pull request as ready for review April 24, 2025 21:51
@decsny decsny removed their request for review April 25, 2025 15:10
@Finomnis
Copy link
Contributor

Finomnis commented May 8, 2025

It does not seem to show up on Win11, at least for me. Can someone else see if it works on Win11 for them?

I get the following:

image

And USB Device Tree Viewer v4.5.0 tells me:

image

But nothing shows up on the "This PC" tab. My phone does show up there and also uses MTP to my knowledge.

I get the following Windows Events in respect to the MTP device:

  • Device install requested

    Device USB\VID_3870&PID_0100&MI_04\8&4b1fd69&0&0004 requires further installation.

  • Device settings not migrated

    Device settings for USB\VID_3870&PID_0100&MI_04\8&4b1fd69&0&0004 were not migrated from previous OS installation due to partial or ambiguous device match.

    Last Device Instance ID: USB\VID_2672&PID_0059&MI_02\9&4705508&0&0002
    Class GUID: {eec5ad98-8080-425f-922a-dabf3de3f69a}
    Location Path:
    Migration Rank: 0xF000FFFFF000F122
    Present: false
    Status: 0xC0000719

  • Driver service added (WUDFWpdMtp)

    Driver Management has concluded the process to add Service WUDFWpdMtp for Device Instance ID USB\VID_3870&PID_0100&MI_04\8&4B1FD69&0&0004 with the following status: 0.

  • Driver service added (WinUsb)

    Driver Management has concluded the process to add Service WUDFWpdMtp for Device Instance ID USB\VID_3870&PID_0100&MI_04\8&4B1FD69&0&0004 with the following status: 0.

Then, the device status is This device is working properly., but no storage device shows up on the My PC tab window.

The device log says nothing spectacular:

(private data removed)

[00:00:00.462,000] <inf> usb_mtp: Init class instance 0x20608
[00:00:00.462,000] <dbg> usb_mtp: usbd_mtp_init: Desc data: Manufacturer: ###, Product: ###, SN: NULL, PktSize: 64
[00:00:00.462,000] <inf> usb_mtp: Init class instance 0x20608
[00:00:00.462,000] <dbg> usb_mtp: usbd_mtp_init: Desc data: Manufacturer: ###, Product: ###, SN: NULL, PktSize: 64
[00:00:00.638,000] <dbg> usb_mtp: usbd_mtp_enable: Configuration enabled
[00:00:00.639,000] <dbg> usb_mtp: usbd_mtp_enable: Ready to receive from HOST
[00:00:31.038,000] <dbg> usb_mtp: usbd_mtp_control_to_host: usbd_mtp_control_to_host: Class request 0x67 (Recipient: 1)
[00:00:31.039,000] <wrn> usb_mtp_class: MTP_REQUEST_GET_DEVICE_STATUS

Any idea? Did somebody get this to work in the past on Win11?


EDIT: I cannot see it on my Ubuntu VM either; this is my dmesg log:

[  173.175339] usb 1-1: new high-speed USB device number 3 using ehci-pci
[  173.430125] usb 1-1: New USB device found, idVendor=2fe3, idProduct=0009, bcdDevice= 4.01
[  173.430136] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  173.430139] usb 1-1: Product: USBD MTP sample
[  173.430142] usb 1-1: Manufacturer: Zephyr Project
[  173.430144] usb 1-1: SerialNumber: 091C100E82923884
[  173.441094] cdc_acm 1-1:1.0: ttyACM0: USB ACM device
[  173.443406] cdc_acm 1-1:1.2: ttyACM1: USB ACM device

(Note: My device also has two 2x CDC ACM)

@ExtremeGTX
Copy link
Collaborator Author

@Finomnis Could you please share the build/zephyr/zephyr.dts and build/zephyr/.config so i can to repro on my end ?

@Finomnis
Copy link
Contributor

@ExtremeGTX can't right now, as it is a commercial product. Will try to reproduce on a dev board and then share.

@Finomnis
Copy link
Contributor

Ok I managed to reproduce it on a teensy40.

All the following examples require CONFIG_MAIN_STACK_SIZE=2048, otherwise you get a stack overflow crash.

I build with west build -b teensy40 samples/subsys/usb/mtp.

I had to add a dtc overlay to samples/subsys/usb/mtp/boards/teensy40.overlay.

The following overlay works:

&usb1 {
	status = "okay";
};

&w25q16jvuxim {
	partitions {
		compatible = "fixed-partitions";
		#address-cells = <1>;
		#size-cells = <1>;

		storage_partition: partition@100000 {
			label = "storage";
			reg = <0x100000  DT_SIZE_M(1)>;
		};
	};
};

/ {
	fstab {
		compatible = "zephyr,fstab";
		lfs1: lfs1 {
			compatible = "zephyr,fstab,littlefs";
			read-size = <32>;
			prog-size = <32>;
			cache-size = <256>;
			lookahead-size = <64>;
			block-cycles = <512>;
			partition = <&storage_partition>;
			mount-point = "/lfs1";
			automount;
			mtp-enabled;
		};
	};
};

USB Tree Viewer:
image

And it shows up on windows:
image
image

On the other hand, this overlay is broken:

&usb1 {
	status = "okay";

	cdc_acm_uart0: cdc_acm_uart0 {
		compatible = "zephyr,cdc-acm-uart";
	};
};

&w25q16jvuxim {
	partitions {
		compatible = "fixed-partitions";
		#address-cells = <1>;
		#size-cells = <1>;

		storage_partition: partition@100000 {
			label = "storage";
			reg = <0x100000  DT_SIZE_M(1)>;
		};
	};
};

/ {
	chosen {
		zephyr,console = &cdc_acm_uart0;
	};

	fstab {
		compatible = "zephyr,fstab";
		lfs1: lfs1 {
			compatible = "zephyr,fstab,littlefs";
			read-size = <32>;
			prog-size = <32>;
			cache-size = <256>;
			lookahead-size = <64>;
			block-cycles = <512>;
			partition = <&storage_partition>;
			mount-point = "/lfs1";
			automount;
			mtp-enabled;
		};
	};
};

USB Tree Viewer show is as follows:
image

And no storage device shows up.

I think (no USB expert though) that as soon as you have multiple endpoints (CDC-ACM and MTP) the device automatically becomes a composite device (or whatever you call it) and that seems to not work with your MTP code.

@Finomnis
Copy link
Contributor

Finomnis commented May 11, 2025

Here are two logfiles, one for the working and one for the broken version:

zephyr_mtp_log_working.txt
zephyr_mtp_log_failed.txt

@Finomnis
Copy link
Contributor

Finomnis commented May 12, 2025

Another bug report: copying text files to and from the device seems to truncate them to 52 bytes.

Implement a basic version of USB MTP (Media transfer protocol) class
which support the necessary MTP commands to handle Dir/file
transfer between a device and host.

Signed-off-by: Mohamed ElShahawi <[email protected]>
Add a sample application that demonstrates how to use the new USB MTP
device class. This shows how to set up the device tree to expose
storage partitions to a host over USB using MTP.
Files/Dirs can be accessed from host without host supportfor the
underlying filesystem like littlefs.

Signed-off-by: Mohamed ElShahawi <[email protected]>
@ExtremeGTX
Copy link
Collaborator Author

Another bug report: copying text files to and from the device seems to truncate them to 52 bytes.

Could you please share the logs with CONFIG_USBD_MTP_LOG_LEVEL_DBG=y enabled.
I tried to copy files of different lengths (10, 54, 190) bytes, also large files of ~600KB of types (png, pdf) to make sure that no corruption happens.

Above tests done while one CDC_ACM instance is enabled in addition to MTP on USB.

Thanks for testing and reporting back :)

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
3 Security Hotspots

See analysis details on SonarQube Cloud

@Finomnis
Copy link
Contributor

Finomnis commented May 13, 2025

Sure :)

I compiled commit 12534fcad7e19672031e1bd6608359c199e10208.

Those are the files I had to modify:

samples/subsys/usb/mtp/prj.conf:

CONFIG_USB_DEVICE_STACK_NEXT=y
CONFIG_USBD_MTP_CLASS=y

CONFIG_SAMPLE_USBD_PRODUCT="USBD MTP sample"
CONFIG_SAMPLE_USBD_PID=0x0009

CONFIG_LOG=y
CONFIG_USBD_LOG_LEVEL_DBG=y
CONFIG_UDC_DRIVER_LOG_LEVEL_DBG=y
CONFIG_USBD_MTP_LOG_LEVEL_DBG=y
CONFIG_LOG_BUFFER_SIZE=131072

CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n

CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_FLASH_LOAD_OFFSET=0x0
CONFIG_FLASH_LOAD_SIZE=0x100000

CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y

CONFIG_USBD_THREAD_STACK_SIZE=3192
CONFIG_MAIN_STACK_SIZE=2048

samples/subsys/usb/mtp/boards/teensy40.overlay:

&w25q16jvuxim {
	partitions {
		compatible = "fixed-partitions";
		#address-cells = <1>;
		#size-cells = <1>;

		storage_partition: partition@100000 {
			label = "storage";
			reg = <0x100000  DT_SIZE_M(1)>;
		};
	};
};

/ {
	fstab {
		compatible = "zephyr,fstab";
		lfs1: lfs1 {
			compatible = "zephyr,fstab,littlefs";
			read-size = <32>;
			prog-size = <32>;
			cache-size = <256>;
			lookahead-size = <64>;
			block-cycles = <512>;
			partition = <&storage_partition>;
			mount-point = "/lfs1";
			automount;
			mtp-enabled;
		};
	};
};

Then I compiled and ran with

west build -b teensy40 samples/subsys/usb/mtp --pristine
west flash

Like previously mentioned, Windows 11, Zephyr SDK 0.17.0.

Here is the log file: (I annotated where I performed an action)

annotated_log_file_round_trip.txt

Note also that the modification time of the file broke in the process:
image

@Finomnis
Copy link
Contributor

Finomnis commented May 13, 2025

Another small insight: When enabling the shell by doing:

CONFIG_SHELL=y
CONFIG_FILE_SYSTEM_SHELL=y

And then running

fs cat /lfs1/loremipsum.txt

Then the file is there and complete.

So the problem must occur during transfer from device to PC.

@Finomnis
Copy link
Contributor

Finomnis commented May 13, 2025

Another bugreport: when connecting the device to the PC, one needs to enter the device directory in the explorer twice before the file shows up. When entering the first time, the directory shows as empty.

@matt-wood-ct
Copy link

matt-wood-ct commented May 14, 2025

I gave your working branch a go on a NXP FRDM RW612 board today, few quirks...
overlay used:

/ {
	fstab {
		compatible = "zephyr,fstab";
		lfs1: lfs1 {
			compatible = "zephyr,fstab,littlefs";
			read-size = <32>;
			prog-size = <32>;
			cache-size = <256>;
			lookahead-size = <64>;
			block-cycles = <512>;
			partition = <&storage_partition>;
			mount-point = "/lfs1";
			automount;
			mtp-enabled;
		};
	};
};

I had to bump the main stack size up to get it to boot, lfs format would fault otherwise.

On Ubuntu 24.04 I had it crashing immediately on plugging in, after a quick debugging session I found that in MTP_CMD_HANDLER(MTP_OP_GET_OBJECT_HANDLES) storage_id was coming through as 0xF which caused a fault on the for loop, I added some error handling to avoid that crash, then I was able to get it to enumerate.

Once enumerated it appeared in my file explorer and somewhat worked, I could browse but writing would cause a crash and leave empty files on the FS. Testing the same setup on windows 10 had a little more success the files written had some data at the start but they were still incomplete (no crashes though).

This is a very good start, but certainly still need a lot of testing and polishing 😄 I will continue to watch this space and test occasionally, very keen to see this feature make it into main.

p.s. I noted the partial file we copied was also exactly 52 bytes long like the @Finomnis 's test file

p.p.s I think your sample needs MAIN_STACK_SIZE=2048 it only works on the pico because that overrides the value to 4096 anyway in https://github.com/ExtremeGTX/zephyr/blob/4c22fc9adade5bab654254626e6e2981d7714f2c/boards/raspberrypi/rpi_pico/Kconfig.defconfig

@ExtremeGTX
Copy link
Collaborator Author

I tried to reproduce on both boards STM32F769i-DISCO and RPI Pico on Windows 10 and 11 using the file attached by @Finomnis and both are working fine, I even tested on another PC which both boards never attached to it before.

Although Teensy 4.0 and FRDM RW612 are different boards, both still use the same NXP UDC Driver, so for me it looks like it is either a problem with NXP UDC Driver or there is an edge case (triggered by NXP UDC) not covered in MTP Implementation, because from the logs I see that communication just stops after first packet which includes the necessary MTP headers + only (52 Bytes of data).

I might have access to one FRDM board and a Nordic board in the next week or the week after, will double check and report back here.

Thanks a lot for your efforts in verifying the implementation, appreciate your support :)

@ExtremeGTX
Copy link
Collaborator Author

ExtremeGTX commented May 16, 2025

@Finomnis regarding file date issue, it is because it will require dependency on Hardware RTC which is a dependency didn't want to have (initially at least, later after stabilizing the implementation we can have a Kconfig to enable the integration) or File system supports storing file properties which is not available in a FS like Littlefs.

Edit: Typo fix

@Finomnis
Copy link
Contributor

@Finomnis regarding file data issue, it is because it will require dependency on Hardware RTC which is a dependency didn't want to have (initially at least, later after stabilizing the implementation we can have a Kconfig to enable the integration) or File system supports storing file properties which is not available in a FS like Littlefs.

I get that, no problem.

Regarding the other issue... I might try to improve the current implementation, but not within the next weeks, I'm busy with life. But later I might join your efforts.

@matt-wood-ct
Copy link

matt-wood-ct commented May 19, 2025

This morning I tried on a nucleo_h723zg board, can confirm it appears to work flawlessly on ubuntu and windows on that platform, it is likely a NXP USB issue. If you could look into that it would be much appreciated as we are interesting in utilising this on an NXP platform on an upcoming project. 😄

@ExtremeGTX
Copy link
Collaborator Author

Hi @dleach02 and @mmahadevan108,
Could you please help ? @Finomnis and @matt-wood-ct are facing transfer issues on NXP Based boards although the same class implementation is verified to work on other platforms (at least ST and RPi).

Thanks in advance :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support the USB MTP feature.
7 participants