USB mtp 介绍

https://en.wikipedia.org/wiki/Media_Transfer_Protocol

from wiki:
MTP是一种高水平的文件传输协议,与通用串行总线大容量存储等通用存储协议相反。这意味着MTP客户端(计算机)看不到构成文件系统的数据结构的字节块数组,而是用文件和文件夹来表示MTP设备。这使得MTP设备可以参与高水平的操作(如更新其元信息索引),同时将文件系统的完整性掌握在自己手中。特别是,掉线传输(例如过早拔掉USB电缆)不会损坏设备文件系统。[6]MTP的非通用性会影响计算机操作系统如何向其他程序和用户呈现MTP设备。
根据其规范,MTP的

  • 主要目的是促进具有瞬态连接的媒体设备之间的通信。[5]
  • 第二个目的是启用对连接设备的命令和控制。[5]电池供电的移动设备可以通过MTP报告其电池电量水平。[6]

该协议最初是为跨USB使用而实现的,但扩展为跨TCP/IP和蓝牙使用。Windows Vista支持TCP/IP上的MTP。带有Windows Vista平台更新的Windows 7和Windows Vista也支持蓝牙上的MTP。[7]连接到MTP设备的主机称为MTP启动器,而设备本身是MTP响应器。[8]

还是 wiki 介绍得更好,其他的随便看看

https://www.cnblogs.com/skywang12345/p/3474206.html

https://wowothink.com/ffcaead/

1、UMS:USB mass storage,USB大容量存储,也被称为UMS,USB MSC。在旧版Android手机上会将其暴露给计算机,有如下几种缺点:

  • USB mass storage是flash driver,外部hd,SD卡和其他USB存储设备使用的标准协议。驱动器使其自身完全对计算机可见,就像它是内部驱动器一样;
  • 这种方式存在问题。无论什么设备访问存储,都需要对其进行独占访问。将存储连接到计算机时,它与设备上运行的Android操作系统断开连接。存储在SD卡或USB存储器上的任何文件或应用程序在连接到计算机时都将不可用,此时是计算机独占;
  • 由于必须可以从Windows设备访问文件系统,因此必须使用FAT文件系统对其进行格式化。 微软不仅拥有对FAT施加的专利,而且FAT也是一个较旧的,较慢的文件系统,没有现代许可系统。

2、MTP:Media Transfer Protocol,媒体传输协议,主要用于传输媒体文件,当Android使用该协议,将其连接到电脑上,会显示一个媒体设备。该协议与USB mass storage的工作方式非常不同。

  • MTP不会将Android设备的原始文件系统暴露给Windows,而是在文件级别运行;
  • Android设备不会将其整个存储设备暴露给Windows。 相反,当将设备连接到计算机时,计算机将查询设备,并且设备会响应其提供的文件和目录列表。
  • Android可以选择它呈现出来的文件,并隐藏系统文件,以便其他人无法查看或修改它们。 如果尝试删除或编辑无法修改的文件,设备将拒绝该请求,将会看到错误消息。
  • 计算机不需要对存储设备进行独占访问,因此无需连接存储,断开连接或为不同类型的数据分别设置分区。Android也可以使用它想要的ext4或任何其他文件系统,但Windows不必了解该文件系统。
  • 在实践中,MTP的功能很像USB mass storage。 例如,MTP设备显示在Windows资源管理器中,因此您可以浏览和传输文件。

libmtp 库

https://github.com/libmtp/libmtp

Initiator and Responder

libmtp implements an MTP initiator, which means it initiate
MTP sessions with devices. The devices responding are known
as MTP responders. libmtp runs on something with a USB host
controller interface, using libusb to access the host
controller.

If you’re more interested in the MTP responders, gadgets like
MP3 players, mobile phones etc, look into:

明显,libmtp 是主机端用的库,

umtprd

buildroot 编译 umtprd 工具,类似于 adbd 的作用。

1
2
# mtp tools
BR2_PACKAGE_UMTPRD=y

configfs 配置 mtp 流程

0. 切换为 device 模式

1
echo device >/proc/cviusb/otg_role

参考 https://wowothink.com/6a85234b/

1. 挂载 configfs

注册了一个名为usb_gadget的configfs子系统。

1
2
mkdir -p /tmp/usb
mount none /tmp/usb -t configfs
1
2
3
4
5
>[root@sg200x]~# tree /tmp/usb/
>/tmp/usb/
>└── usb_gadget

>1 directory, 0 files

2. 创建复合设备

mkdir usb_gadget/cvitek 创建名为 cvitek 的usb复合设备。

1
mkdir -p /tmp/usb/usb_gadget/cvitek

在 cvitek 目录下会创建很多属性,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@sg200x]/tmp/usb/usb_gadget# tree .
.
└── cvitek
├── UDC
├── bDeviceClass
├── bDeviceProtocol
├── bDeviceSubClass
├── bMaxPacketSize0
├── bcdDevice
├── bcdUSB
├── configs
├── functions
├── idProduct
├── idVendor
├── max_speed
├── os_desc
│   ├── b_vendor_code
│   ├── qw_sign
│   └── use
└── strings

5 directories, 13 files

2.1 配置PID和VID

1
2
echo 0x3346 >/tmp/usb/usb_gadget/cvitek/idVendor
echo 0x1003 >/tmp/usb/usb_gadget/cvitek/idProduct

2.2 创建并配置 strings 子目录

配置 strings 首先必须设置language,这里设置为0x0409表示使用的是en-us语言。

1
2
3
4
5
# Set the product information string
mkdir /tmp/usb/usb_gadget/cvitek/strings/0x409
echo "Cvitek" >/tmp/usb/usb_gadget/cvitek/strings/0x409/manufacturer
echo "USB Com Port" >/tmp/usb/usb_gadget/cvitek/strings/0x409/product
echo "0123456789" >/tmp/usb/usb_gadget/cvitek/strings/0x409/serialnumber
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>[root@sg200x]/tmp/usb/usb_gadget# tree .
>.
>└── cvitek
├── UDC
├── bDeviceClass
├── bDeviceProtocol
├── bDeviceSubClass
├── bMaxPacketSize0
├── bcdDevice
├── bcdUSB
├── configs
├── functions
├── idProduct
├── idVendor
├── max_speed
├── os_desc
│   ├── b_vendor_code
│   ├── qw_sign
│   └── use
└── strings
└── 0x409
├── manufacturer
├── product
└── serialnumber

>6 directories, 16 files

2.3 创建configuration和字符串

创建配置 c.1

1
mkdir /tmp/usb/usb_gadget/cvitek/configs/c.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@sg200x]/tmp/usb/usb_gadget# tree .
.
└── cvitek
├── UDC
├── bDeviceClass
├── bDeviceProtocol
├── bDeviceSubClass
├── bMaxPacketSize0
├── bcdDevice
├── bcdUSB
├── configs
│   └── c.1
│   ├── MaxPower
│   ├── bmAttributes
│   └── strings
├── functions
├── idProduct
├── idVendor
├── max_speed
├── os_desc
│   ├── b_vendor_code
│   ├── qw_sign
│   └── use
└── strings
└── 0x409
├── manufacturer
├── product
└── serialnumber

8 directories, 18 files

设置配置名。

1
2
mkdir /tmp/usb/usb_gadget/cvitek/configs/c.1/strings/0x409
echo "config1" >/tmp/usb/usb_gadget/cvitek/configs/c.1/strings/0x409/configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[root@sg200x]/tmp/usb/usb_gadget# tree .
.
└── cvitek
├── UDC
├── bDeviceClass
├── bDeviceProtocol
├── bDeviceSubClass
├── bMaxPacketSize0
├── bcdDevice
├── bcdUSB
├── configs
│   └── c.1
│   ├── MaxPower
│   ├── bmAttributes
│   └── strings
│   └── 0x409
│   └── configuration
├── functions
├── idProduct
├── idVendor
├── max_speed
├── os_desc
│   ├── b_vendor_code
│   ├── qw_sign
│   └── use
└── strings
└── 0x409
├── manufacturer
├── product
└── serialnumber

9 directories, 19 files

Set the MaxPower of USB descriptor

1
echo 120 >/tmp/usb/usb_gadget/cvitek/configs/c.1/MaxPower

3. 创建functions

一个USB复合设备会有多个功能,每个功能由function来表示,所谓的function就是USB设备支持的功能。当执行mkdir functions/mass_storage.0的时候,会调用function_make()函数,调用usb_get_function_instance()函数传入mass_storage字段,将会从现有的function list中找到与之匹配的function。现有的function list是由drivers/usb/gadget/function/f_xxx.c中进行添加的,这就将f_xxx.c联系起来了。

3.1 创建 mtp functions

1
mkdir /tmp/usb/usb_gadget/cvitek/functions/mtp.usb0

内核还没开启相应配置的,就会报错。USB_CONFIGFS_F_MTP=y

1
2
3
[root@sg200x]/tmp/usb/usb_gadget# mkdir /tmp/usb/usb_gadget/cvitek/functions/mtp
.usb0
mkdir: can't create directory '/tmp/usb/usb_gadget/cvitek/functions/mtp.usb0': No such file or directory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[root@sg200x]/tmp/usb/usb_gadget# tree .
.
└── cvitek
├── UDC
├── bDeviceClass
├── bDeviceProtocol
├── bDeviceSubClass
├── bMaxPacketSize0
├── bcdDevice
├── bcdUSB
├── configs
│   └── c.1
│   ├── MaxPower
│   ├── bmAttributes
│   └── strings
│   └── 0x409
│   └── configuration
├── functions
│   └── mtp.usb0
├── idProduct
├── idVendor
├── max_speed
├── os_desc
│   ├── b_vendor_code
│   ├── qw_sign
│   └── use
└── strings
└── 0x409
├── manufacturer
├── product
└── serialnumber

10 directories, 19 files

4. 将func和config关联起来

执行ln -s usb_gadget/cvitek/functions/mtp.usb0 usb_gadget/cvitek/configs/c.1命令将新添加的mtpfunctions添加到configuration对应的function list中,表示当前usb复合设备中新增了一个function。这时候调用的是config_usb_cfg_link()函数。至此,functionsconfiguration关联起来了。接下来要将configuration与特定的UDC设备连接起来。

1
ln -s /tmp/usb/usb_gadget/cvitek/functions/mtp.usb0 /tmp/usb/usb_gadget/cvitek/configs/c.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[root@sg200x]/tmp/usb/usb_gadget# tree .
.
└── cvitek
├── UDC
├── bDeviceClass
├── bDeviceProtocol
├── bDeviceSubClass
├── bMaxPacketSize0
├── bcdDevice
├── bcdUSB
├── configs
│   └── c.1
│   ├── MaxPower
│   ├── bmAttributes
│   ├── mtp.usb0 -> ../../../../usb_gadget/cvitek/functions/mtp.usb0
│   └── strings
│   └── 0x409
│   └── configuration
├── functions
│   └── mtp.usb0
├── idProduct
├── idVendor
├── max_speed
├── os_desc
│   ├── b_vendor_code
│   ├── qw_sign
│   └── use
└── strings
└── 0x409
├── manufacturer
├── product
└── serialnumber

11 directories, 19 files

5. 绑定到UDC,使能gadget

执行echo xxx > usb_gadget/g1/UDC命令,就会调用gadget_dev_desc_UDC_store()函数,这里面调用usb composite framework中的usb_gadget_probe_driver()函数将gadget driver与USB Controller Driver绑定,这里的Gadget Driver就是与我们创建的usb复合设备对应的驱动,接下来就会走一系列的bind流程。

1
2
3
# Start the gadget driver
CVI_UDC=$(ls /sys/class/udc/ | awk '{print $1}')
echo ${CVI_UDC} >/tmp/usb/usb_gadget/cvitek/UDC

汇总

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 0. 切换为 device 模式
echo device >/proc/cviusb/otg_role
# 1. 挂载 configfs
mkdir -p /tmp/usb
mount none /tmp/usb -t configfs
# 2. 创建复合设备
mkdir -p /tmp/usb/usb_gadget/cvitek
# 2.1 配置PID和VID
echo 0x3346 >/tmp/usb/usb_gadget/cvitek/idVendor
echo 0x1003 >/tmp/usb/usb_gadget/cvitek/idProduct
# 2.2 创建并配置 strings 子目录
mkdir /tmp/usb/usb_gadget/cvitek/strings/0x409
echo "Cvitek" >/tmp/usb/usb_gadget/cvitek/strings/0x409/manufacturer
echo "USB Com Port" >/tmp/usb/usb_gadget/cvitek/strings/0x409/product
echo "0123456789" >/tmp/usb/usb_gadget/cvitek/strings/0x409/serialnumber
# 2.3 创建configuration和字符串
mkdir /tmp/usb/usb_gadget/cvitek/configs/c.1
mkdir /tmp/usb/usb_gadget/cvitek/configs/c.1/strings/0x409
echo "config1" >/tmp/usb/usb_gadget/cvitek/configs/c.1/strings/0x409/configuration

echo 120 >/tmp/usb/usb_gadget/cvitek/configs/c.1/MaxPower
# 3. 创建functions
mkdir /tmp/usb/usb_gadget/cvitek/functions/mtp.usb0
# 4. 将func和config关联起来
ln -s /tmp/usb/usb_gadget/cvitek/functions/mtp.usb0 /tmp/usb/usb_gadget/cvitek/configs/c.1

##########
mkdir /dev/ffs-umtp
mount -t functionfs mtp /dev/ffs-umtp
# Start the umtprd service
umtprd &

# 5. 绑定到UDC,使能gadget
CVI_UDC=$(ls /sys/class/udc/ | awk '{print $1}')
echo ${CVI_UDC} >/tmp/usb/usb_gadget/cvitek/UDC

umptrd 源码

https://github.com/viveris/uMTP-Responder/tree/master/conf

官方提供了一个配置脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/bin/sh

# FunctionFS uMTPrd startup example/test script
# Must be launched from a writable/temporary folder.

modprobe libcomposite

mkdir cfg
mount none cfg -t configfs

mkdir cfg/usb_gadget/g1
cd cfg/usb_gadget/g1

mkdir configs/c.1

mkdir functions/ffs.mtp
# Uncomment / Change the follow line to enable another usb gadget function
#mkdir functions/acm.usb0

mkdir strings/0x409
mkdir configs/c.1/strings/0x409

echo 0x0100 > idProduct
echo 0x1D6B > idVendor

echo "01234567" > strings/0x409/serialnumber
echo "Viveris Technologies" > strings/0x409/manufacturer
echo "The Viveris Product !" > strings/0x409/product

echo "Conf 1" > configs/c.1/strings/0x409/configuration
echo 120 > configs/c.1/MaxPower

ln -s functions/ffs.mtp configs/c.1
# Uncomment / Change the follow line to enable another usb gadget function
#ln -s functions/acm.usb0 configs/c.1

mkdir /dev/ffs-umtp
mount -t functionfs mtp /dev/ffs-umtp
# Start the umtprd service
umtprd &

cd ../../..

sleep 1

# enable the usb functions
ls /sys/class/udc/ > cfg/usb_gadget/g1/UDC
1
2
mount -t functionfs mtp /dev/ffs-umtp
mount: mounting mtp on /dev/ffs-umtp failed: No such device

CONFIG_USB_FUNCTIONFS=y 可解决上面的问题。