Linux USB mtp
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:
- MeeGo:s Buteo Sync:
https://github.com/nemomobile/buteo-mtp
https://wiki.merproject.org/wiki/Buteo/MTP - Android has an MTP responder implementation:
https://android.googlesource.com/platform/frameworks/base/+/master/media/jni/ - Ubuntu/Ricardo Salveti has mtp-server and libmtp-server going:
https://code.launchpad.net/~phablet-team/mtp/trunk
https://bazaar.launchpad.net/~phablet-team/mtp/trunk/files
明显,libmtp 是主机端用的库,
umtprd
buildroot 编译 umtprd 工具,类似于 adbd 的作用。
1 | # mtp tools |
configfs 配置 mtp 流程
0. 切换为 device 模式
1 | echo device >/proc/cviusb/otg_role |
1. 挂载 configfs
注册了一个名为usb_gadget的configfs子系统。
1 | mkdir -p /tmp/usb |
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 | echo 0x3346 >/tmp/usb/usb_gadget/cvitek/idVendor |
2.2 创建并配置 strings 子目录
配置 strings 首先必须设置language
,这里设置为0x0409
表示使用的是en-us
语言。
1 | # Set the product information string |
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 | mkdir /tmp/usb/usb_gadget/cvitek/configs/c.1/strings/0x409 |
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 | [root@sg200x]/tmp/usb/usb_gadget# mkdir /tmp/usb/usb_gadget/cvitek/functions/mtp |
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
命令将新添加的mtp
的functions
添加到configuration
对应的function list中,表示当前usb复合设备中新增了一个function。这时候调用的是config_usb_cfg_link()
函数。至此,functions
和configuration
关联起来了。接下来要将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 | # Start the gadget driver |
汇总
1 | # 0. 切换为 device 模式 |
umptrd 源码
https://github.com/viveris/uMTP-Responder/tree/master/conf
官方提供了一个配置脚本。
1 |
|
1 | mount -t functionfs mtp /dev/ffs-umtp |
CONFIG_USB_FUNCTIONFS=y
可解决上面的问题。