文章代号:android-qemu-usb-withoutroot
教程部分操作参考: https://github.com/termux/termux-packages/issues/19635
视频教程
哔哩哔哩:BV1YSenefEo9
前言
几个月前我做过一期教程(BV1PZ42187NY)提供了一种在手机Qemu中直通USB的思路,不过呢那个条件有些苛刻,比如需要Root,这就已经将很多人“拒之门外”了;使用起来也有诸多限制,例如不支持热插拔(这个真的很烦,稍微不注意线碰掉了就得重开虚拟机),如果你用的VirtualHere方案的,系统要是Windows10 x64以上,还必须有WiFi网络。
前几天偶然发现Termux里有个名为termux-usb的软件包很有意思,使用Android的OTG API来不Root获取USB访问权限,就突发奇想:能不能和Qemu配合使用呢?答案是真的可以。而且不用网络、不要求操作系统、支持热插拔,你愿意的话还能直通局域网内其它设备的USB,所以即使你已经Root也可以用起来,比之前那个方法好用100倍。
(如果你已经Root的话,那就不用termux-usb了,同时系统也不必支持OTG API。把我教程的命令改一下使用sudo运行usbredicter再结合那个视频的方法自己研究。就不详细教学了,既然你已经Root了我相信这些小技能你还是会的)
大致原理如下: 使用一个名为usbredicter的软件包通过termux-usb API获取USB访问权限,在本地指定网络端口创建一个接口,proot容器里的Qemu通过这个网络接口获取USB使用权。
注意事项(一定要看)
没有Root的话手机必须支持OTG API(只要不是特别古董的一般都会支持)
如果你的手机是新款并且在接下来的某一步操作没反应,那可能是你的系统阉割了这个特性,这里建议刷类原生或者国际版
设置里如果有开启OTG权限的选项一定要打开
每次使用都要按照5.2的步骤重新授权
如果有多个USB设备可以在屏幕左侧边缘一次性下滑再左滑呼出侧边栏,点击
New Session
创建新的终端并重复操作步骤Qemu那里如果报
Connection refused
就去检查下usbredirect进程是否意外退出
环境要求
建议在Termux中的Proot/Chroot容器中运行的原生Qemu
注:不支持Limbo虚拟机,因为Qemu版本太低了
(如果你因为这个教程入坑Termux,可以先看这篇专栏 进行配置再网上搜索“termux使用proot运行qemu教程”,然后到cv21056621 学一下如何启动qemu。前往F-droid下载Termux的APK安装包)
准备工作
1.到这里下载并安装Termux API的apk
2.有条件的打开你的梯子
正式部分
安装termux-usb
1 | pkg install termux-api libusb clang -y |
授权USB访问
连接你的USB设备,输入termux-usb -l
列出设备。
输出示例:
[
"/dev/bus/usb/001/003"
]
/dev/bus/usb/001/003
代表USB设备的文件路径(Linux下一切皆文件),记住等下会用到
这里因人而异,记住你自己的
接着获取USB访问权限
1 | termux-usb -r /dev/bus/usb/001/003 |
等一两秒会弹出一个授权框,点击确认。
如果输出Access granted
代表授权成功;如果等了很久还是没有输出的话可以先按Ctrl+C然后参照Termux Wiki验证可访问性。
验证可访问性
先编译一下测试程序
1 | wget 'https://gist.githubusercontent.com/bndeff/8c391bc3fd8d9f1dbd133ac6ead7f45e/raw/6d7174a129301eeb670fe808cd9d25ec261f7f9e/usbtest.c' |
1 | gcc usbtest.c -lusb-1.0 -o usbtest |
接着运行,记得把路径改成自己的
1 | termux-usb -e ./usbtest /dev/bus/usb/001/003 |
稍等片刻,会看到指定路径USB设备的基本信息,成功输出:
Vendor ID: 0781
Product ID: 5567
Manufacturer: USB
Product: SanDisk 3.2Gen1
Serial No: 0401d642696d418d92d6e77f095c4a788cba4feb64bafc16dd8b083c0746389ed8f40000000000000000000073564d92ff0e7f1867558107852f280a
编译并安装usbredicter
因为原版的usbredicter不支持termux-usb,尝试运行会输出Failed to init libusb
错误,因此需要修改一下代码以适配termux-usb。
不过不必担心,已经有现成的修改代码(虽然不是我写的),我放在链接里了,文件名是usbredicter.c,先下载下来等会用。
使用git克隆usbredir仓库
先输入pkg install git -y
安装git,然后克隆
1 | git clone https://gitlab.freedesktop.org/spice/usbredir.git |
复制以下命令安装meson和ninja
1 | pkg install python3 ninja -y |
修改源代码
复制你下载下来的usbredirect.c的完整路径(可以使用MT管理器查看文件属性,在弹出的窗口长按文件路径获取),然后使用mv将其覆盖,例如
1 | mv /storage/emulated/0/Download/usbredirect.c usbredir/tools/usbredirect.c |
使用nano编辑器(可以输入pkg install nano -y
安装)编辑meson.build
1 | nano usbredir/meson.build |
使用方向键移动光标,定位到第7行的warning_level=1
,将1改为0
依次按Ctrl+X、Y、回车保存并退出。
编译usbredir
1 | mkdir usbredir/build |
运行~/usbredir/build/tools/usbredirect
,正常会输出一段帮助文本。
注意不要移动任何可执行文件,否则会报错。
创建USB设备网络接口
使用termux-usb运行usbredirect:
1 | termux-usb -e "/data/data/com.termux/files/home/usbredir/build/tools/usbredirect --device /dev/bus/usb/001/003 --as 127.0.0.1:23456" /dev/bus/usb/001/003 |
注意路径要改成自己的,两处都要改;网络端口在1024-65535之间随便取一个即可。
等大约半分钟,看到终端输出USB设备的基本信息即为创建成功。
配置Qemu启动脚本
在屏幕左侧边缘一次性下滑再左滑呼出侧边栏,点击New Session
创建新的终端,在新的终端下,进入你的容器,并编辑Qemu启动脚本
在启动脚本末尾加上:
1 | -monitor stdio \ |
在-chardev
选项中,将port
替换为自己的端口号。如果有多个USB设备,需要为每个设备添加一行-chardev
配置。
id
字段用来为每个设备指定一个名称。你可以自由选择这个名称。请确保在-device
选项中,chardev=
后面的名称与-chardev
选项中指定的名称一致。
-device
选项中的id
是另一个独立的标识符。请确保这个id
与-chardev
选项中的id
不同,以避免冲突。
以上所有id都要记住,等下热插拔的时候要用
注意要给Qemu一个USB控制器。
启动Qemu
保存退出后,运行你的启动脚本。
启动的时候如果看到qemu-system-x86_64: usb-redir: attaching high speed device 0781:5567 version 1.0 class 00
,那么恭喜你,成功了!
按下回车后,可以查看qemu内usb设备的基本信息
1 | (qemu) info usb |
示例输出:
Device 0.1, Port 1, Speed 480 Mb/s, Product QEMU USB Tablet
Device 0.2, Port 2, Speed 480 Mb/s, Product USB Redirection Device, ID: usbredirdev1
USB Redirection Device
就是你的USB设备,注意前面的speed如果不是480而是1.5,说明USB设备可能断开连接了,重复教程的步骤重新连接。
一切顺利的话,开机后就能看到你的USB设备啦!
附:热插拔
如果你在虚拟机运行的时候把USB设备拔掉并重新插回来的时候,你会发现Qemu无法识别了!这是因为当你拔掉时,Android会撤销Termux对这个USB设备的访问权限,所以当你这个时候回到终端时就会看到usbredicter报错退出了。别担心,重新授权一遍即可。
不过呢,你也需要在QEMU Monitor中删除现有的USB Redictertion设备设备并重新添加。
步骤如下:
(qemu) device_del usbredirdev1
(qemu) chardev-add socket,host=127.0.0.1,port=23456,id=usbredirchardev2
(qemu) device_add usb-redir,chardev=usbredirchardev2,id=usbredirdev1,debug=3
懒得打字了,让chatgpt帮我解释下命令吧:
(qemu) device_del usbredirdev1
- 作用: 删除一个叫做
usbredirdev1
的 USB 设备。 - 解释: 如果你有一个 USB 设备在虚拟机里运行,这个命令会把它移除掉。
- 作用: 删除一个叫做
(qemu) chardev-add socket,host=127.0.0.1,port=23456,id=usbredirchardev2
- 作用: 创建一个新的通信通道,用来连接虚拟机和主机。
- 解释: 这个命令设置了一个新的连接方式,虚拟机将通过这个连接方式与主机上的服务进行交流。连接的地址是
127.0.0.1
,端口号是23456
,这个通道的名字叫usbredirchardev2
。
(qemu) device_add usb-redir,chardev=usbredirchardev1,id=usbredirdev1,debug=3
- 作用: 添加一个新的 USB 设备到虚拟机。
- 解释: 这个命令把一个新的 USB 设备添加到虚拟机中。这个设备通过之前设置的连接通道
usbredirchardev1
与主机上的服务沟通。这个设备的名字是usbredirdev1
,并且设置了更详细的调试信息以帮助解决问题。
注意,在使用 chardev-add
时,ID 必须取一个另外的,否则会报Duplicate ID。而 device_add
的 ID 则可以与启动脚本中的相同。
每次拔出设备后usbredirect就会退出,要手动回到之前的那个终端运行。