中兴 F50 从 B16 降级 B09 并刷入 OpenWrt
在上一篇文章寻找一台理想的迷你 5G CPE 中,笔者提到了手上的中兴 F50,以及用 USB 网卡扩展有线连接的尝试。最近发现了 mu300-linux 项目,可以在 F50 上运行独立的 Linux 系统,支持 Ubuntu 和 OpenWrt,还能保留原来的 Android 系统,于是决定继续折腾一下这台设备。
不过,这台 F50 使用的是官方 B16 固件,最初既没有 root,也没有稳定可用的 ADB。手边只有一台 Mac,而网上不少教程又依赖 Windows 工具。最后走通的路线是:通过 USB 冷启动窗口进入下载模式,先将完整固件降级到 B09,再取得 Magisk root,最后安装带 LuCI 的 OpenWrt。
完成后,F50 默认启动 OpenWrt,USB 网络、ADB、SSH 和网页管理均可使用;原厂 B09 保留在 A 槽,可以切换回去。随后为了继续尝试 USB 网卡,笔者又将内核从 5.4 切换到 6.18.60,使 MU300 界面的 USB Host 控制接口可用。本文记录从 B16 开始的实际过程,以及其中几个踩到的坑。
设备与安装方案
本次使用的环境如下:
| 项目 | 本次环境 |
|---|---|
| 设备 | 中兴 F50,系统设备代号 MU300 |
| 原始固件 | F50_FLYMODEM_ZYV1.0.0B16 |
| 存储 | 64 GB 版 eMMC,原有 userdata 约 20 GiB |
| 操作电脑 | Apple Silicon Mac |
| 连接方式 | USB 数据线,冷启动时同时负责供电 |
| 降级后的固件 | F50_FLYMODEM_ZYV1.0.0B09 |
| Linux 项目版本 | mu300-linux v2026.11.02.1 |
| 系统 | OpenWrt 25.12.6,带 LuCI |
| 内核 | 首次安装 5.4.254,后切换至主线 6.18.60 |
整个过程可以分成四段:
flowchart LR
A["官方 B16"] -->|"USB 下载模式降级"| B["官方 B09"]
B -->|"匹配的 boot 与 TrustOS"| C["B09 + Magisk root"]
C -->|"USB ADB 安装"| D["OpenWrt 默认启动<br/>保留 B09 Android"]
D -->|"保留配置、更换内核"| E["Linux 6.18.60<br/>USB Host 控制接口可用"]这里不要求 F50 自己能够访问互联网。固件和 Linux 镜像由 Mac 下载,设备端通过 USB 接收文件;即使没有 SIM 卡或者无线网络上游,仍然可以完成安装。
需要提前说明的是,这是一台 64 GB F50 的实测记录。后面的存储布局来自这台机器,不能直接套用到其他版本或固件上。降级流程也设置了恢复出厂请求,因此应当先备份原有数据。
B16:难以访问 ADB 和取得 root
最开始,笔者尝试直接在 B16 上开启 ADB。原厂后台中的 usb_port 已经显示开启,但 Mac 只能看到 USB 网络和文件传输相关接口,adb devices 没有列出设备。
后来通过手机连接 F50 的 Wi-Fi,在后台执行了一次「关闭并应用 → 开启并应用」,然后让 Mac 提前监听,再将 F50 断电重连,终于捕获到了开机初期短暂出现的 ADB。此时可以读取固件版本和启动槽,执行 id 得到的却仍然是普通的 uid=2000(shell);过一会儿,ADB 又消失了。
这说明了两个问题:后台的 USB 开关状态打开后,运行时也只在启动后的时间窗口暴露 ADB;即使能进入 ADB shell,也无法直接取得 root。
公开 Windows 工具中的一种做法,同样需要利用这个短暂的 USB ADB 窗口,再继续处理服务配置和权限。这次虽然抓到了普通 ADB shell,但后续 root 没有成功,笔者于是转向降级 B09 的路线。
在 Mac 上进入下载模式
降级主要参考 zte-f50-toolkit。这个项目除了 Windows 工具,也提供了 mac-f50 目录,可以在 macOS 上使用原生 spd_dump。
本次实际使用的是此前已准备好的 DashPlay 中的 Mac ARM64 版 spd_dump。对应的底层源码固定在 spreadtrum_flash 的 d24c21a 版本,并带有读取原始 user_partition 的补丁。后面的备份和刷写由本地脚本控制;并没有直接运行 toolkit 的通用全量刷机脚本。
spd_dump 通过展锐平台的早期下载接口与设备通信。实际使用的连接入口如下,命令中的 spd_dump 指前面固定版本的工具:1
./spd_dump --wait 600 --kickto 2 exec
这里最重要的是操作顺序:先启动工具等待连接,再让设备冷启动。笔者的做法是拔掉 F50 的 USB 电源,等待约 5 秒,然后重新插回 Mac。工具在开机窗口中捕获设备,完成 BootROM 握手,并加载 FDL1、FDL2。FDL 是下载阶段运行在设备内存中的加载程序,后续分区读写由它完成。
本次不需要拆机短接测试点。不过,普通的软件重启与冷启动并不等价:在后面的 root 阶段,笔者尝试过先监听、再执行 adb reboot,结果只是正常回到 B09,没有进入所需的下载模式。最后仍然是断电重插成功。
降级前先备份
进入 FDL 后,先读取 GPT,确认真实的分区名称、大小和偏移,再逐项备份。例如,以下是 spd_dump 交互提示符中的只读命令:1
2partition_list partition_list.xml
read_part user_partition 0 32768 primary-gpt.bin
第一条导出分区列表,第二条读取本机磁盘开头的 32 KiB,保存主 GPT 所在区域。其余分区按各自的真实偏移和长度读取;每个输出文件使用新的名称,避免覆盖已有备份。
本次备份包括 75 个 GPT 分区、主副两份 GPT,以及约 20 GiB 的 userdata。另外还重复读取了两份 256 KiB 的 SPL 别名,检查读回内容是否一致。每个备份文件都记录长度和 SHA256,后续操作以这份分区表和备份为依据。
这份备份仍然不是全芯片镜像:它不包含完整的 eMMC boot0、boot1、RPMB,也没有把原本未分配的所有空隙都复制出来。对于设备专属的 NV、校准和持久化分区,应当保留本机数据,不能用网上固件包里同名文件随意替换。
固件使用 toolkit 的 releasev1 压缩包。本次下载文件大小为 1,022,678,668 字节,SHA256 为:1
33cfc91eb96f67f49d5b722aaf06c7ccb5ead534f38f896191a72e22c18c1af7
解包后,zte-f50-b09 目录保存 B09 固件,bin 目录中还有后续 root 所需的文件。此时先处理原厂降级,root 镜像留到 B09 正常启动之后再使用。
最容易踩的坑:A/B 分区并不一定按名字写入
F50 有不少以 _a、_b 结尾的分区。直觉上,向 boot_a 写入镜像,应该只修改 A 槽。但这台设备使用本次 FDL 工具时,实际观察到了向一个分区名发出一次写入命令,却同时改变 A/B 两份分区的行为。
这一点在早期 B16 root 镜像测试中就已经暴露出来。发现两个 boot 分区同时变化后,笔者停止了后续步骤,没有继续写 TrustOS,也没有启动候选镜像。随后回写原厂 A 镜像,两槽都成为 A 的副本,设备恢复了 B16 正常启动;原始 B 镜像仍然单独保存在备份中。
于是,后续操作中每写完一组,就按 GPT 中的真实物理偏移分别读取 A、B,确认究竟改了哪里。
这也是为什么本次没有直接运行上游 flash-b09-mac.sh。所审阅版本的脚本会遍历目录里的 .bin 文件,而固件包里不只有通用系统镜像,也有本机应当保留的持久化区域。对于这台已经确认存在成对写入行为的设备,必须谨慎操作。
另一个容易忽略的问题在 super 分区。B09 包中的可用 Android 逻辑系统位于 A 槽。 如果最后仍然选择 B 启动,即使所有镜像都写入成功,也可能无法进入系统。这个问题在项目的 PR #4 中有相关讨论,本次也独立检查了下载固件的 super 元数据。
因此,降级需要一起解决固件配套和启动槽选择,不能只把 B09 的 boot 或 trustos 单独塞进 B16 的启动链里。
刷入 B09,并确认原厂系统正常启动
本次刷写清单包含 18 组固件,对应 34 个实际分区。实际执行顺序为:1
2
3
4
5
6
7super → metadata
→ nr_modem → nr_phy → pm_sys
→ dtbo → vendor_boot → boot
→ vbmeta_odm → vbmeta_product → vbmeta_system
→ vbmeta_system_ext → vbmeta_vendor → vbmeta
→ teecfg → sml → trustos → uboot
→ misc
对于成对的固件分区,使用包内对应的 A 组原厂镜像,先发出一次 A 槽命名写入,再从物理位置完整读回两槽。由于本机的成对写入行为,实际没有必要再补一次 B 槽写入。单个分区则按它自己的范围核对。
misc 放在最后处理。它既保存恢复模式使用的 Bootloader Control Block,也保存 A/B 启动控制记录。笔者先读取本机完整的 misc,只替换本次需要的恢复出厂请求和启动控制内容,设置 A 槽可启动、B 槽不可启动,计算并核对启动控制记录的 CRC,再写回和验证其余内容没有变化。
这里还遇到一个命令语义上的坑:这个工具分支里的 e userdata 并不只是擦除一个叫 userdata 的分区,它还会写入恢复出厂请求并擦除 persist。本次没有调用这个命令,而是保留 persist,单独构造恢复请求。
完成后,重新检查了 79 个范围:固件镜像与预期一致,保护分区、GPT 和 SPL 未变化,userdata 的首尾抽样也未变化。这些检查描述的是重启之前的状态;全部通过后才让设备重启。
手机重新连上 F50 的 Wi-Fi 后,管理后台可以正常打开,版本显示为 B09。刷后笔者还手动重新开启了一次 usb_port,此后 Mac 上的 USB ADB 才稳定可用。
接着在 Mac 上检查:1
2
3
4
5adb devices
adb shell getprop ro.build.display.id
adb shell getprop ro.boot.slot_suffix
adb shell getprop sys.boot_completed
adb shell id
对应结果为:1
2
3
4F50_FLYMODEM_ZYV1.0.0B09
_a
1
uid=2000(shell) ...
到这一步,完成的是官方 B09 降级,还没有 root。
为 B09 准备匹配的 Magisk 与 TrustOS
后续使用同一个 toolkit 包中的两个文件:
| 文件 | 用途 |
|---|---|
bin/magiskf50b09.img | 为 B09 准备的 Magisk boot 镜像,大小 64 MiB |
bin/f50b09_t.bin | B09 对应的 TrustOS 修补镜像,大小 5,178,856 字节 |
Magisk 负责在 Android 启动过程中提供 root;TrustOS 的修改则关系到这套设备对修改后启动镜像的接受条件。两者需要与当前 B09 固件配套。
本次先核对了 Magisk 镜像中的内核、备份的原始 init 等内容,确认与已经刷入的 B09 相符,再准备实际写入文件。TrustOS 文件还有一个细节:原分区是 6 MiB,补丁文件比整个分区短。笔者使用新读取的原厂 B09 分区作为基础,用补丁覆盖前缀,保留后面的原始字节,最终得到一个完整的 6 MiB 镜像。其构造关系为:1
patched_trustos = patch + stock_trustos[len(patch):]
这里的 stock_trustos 必须是已经核对过的 B09 原厂内容。实际写入前还检查了输入大小、SHA256 和输出镜像的完整长度。
随后再次先启动 FDL 监听,再断电约 5 秒、重新插入 USB。root 阶段只执行了两组写入:先写准备好的 TrustOS,再写 Magisk boot,并分别完整读取 A/B 两槽确认结果。GPT、SPL、NV 和 misc 都不在本次修改范围内。
与降级阶段一样,这里的 FDL 命名写入会影响两槽,所以必须核对两份。这个行为属于本次 FDL 路径;后面通过 Android 的块设备节点写入 OpenWrt 时,还需要重新确认 A、B 对应的是不同设备。
Magisk 装上了,为什么 su 仍然不可用?
刷完重启后,B09 能够正常启动,Magisk 相关进程也已经出现,但普通 shell 中一开始仍然找不到可直接使用的 su。检查后发现,Magisk 的临时文件位于 /debug_ramdisk,管理应用也提示「需要修复运行环境」。
这次从 Magisk 官方 Canary 28103 发布页单独下载了 app-release.apk,核对它与 boot 中的版本相符,并在本地命名为 Magisk-canary-28103.apk。安装管理应用并打开:1
2adb install -r Magisk-canary-28103.apk
adb shell am start -n com.topjohnwu.magisk/.ui.MainActivity
F50 没有可操作的实体屏幕,但 Android 仍然有显示界面。本次通过 ADB 唤醒、截屏查看画面,再模拟点击来操作 Magisk:1
2adb shell input keyevent KEYCODE_WAKEUP
adb exec-out screencap -p > magisk-screen.png
在 Mac 上打开截图,确认当前按钮的位置,再使用 adb shell input tap X Y 点击;这里的 X Y 应当按实际画面确定。
通过这种方式确认「修复运行环境」,等待它完成并自动重启。这个动作是补齐 Magisk 的运行环境。
重启后,再从 ADB 请求 root,并在 Magisk 的超级用户界面中为 Shell 授权:1
adb shell su -c id
实际输出包含:1
uid=0(root) gid=0(root) groups=0(root) context=u:r:magisk:s0
到这里才算真正取得了可用的 Android root。
准备 OpenWrt:先确认存储空间
取得 root 后,才轮到最初想使用的 mu300-linux。本次使用 v2026.11.02.1,源码提交为 041819a4c75e9244fa42e1e405212f8d61e5fed5,并关闭安装器自更新,避免准备时检查的代码与实际执行的代码不同。
项目的电脑端安装器需要 adb、Python 3、lz4 和 curl。Mac 上准备依赖、下载固定版本源码,可以使用:1
2
3
4
5
6
7
8
9
10brew install --cask android-platform-tools
brew install python lz4
git clone --branch v2026.11.02.1 --depth 1 \
https://github.com/dikeckaan/mu300-linux.git
cd mu300-linux
export MU300_NO_SELF_UPDATE=1
export MU300_RELEASE=v2026.11.02.1
export MU300_GUI=0
安装器会从这台设备提取需要的 Wi-Fi、蓝牙固件和 Android 基带用户态组件,再配合 Linux 内核和 OpenWrt rootfs 工作。
开始安装前,笔者重新确认了 B09、A 槽、su 权限,以及 uboot、sml、teecfg、trustos、vbmeta 五组启动链的两槽内容一致。随后检查 GPT 和未分配区域。
这台 64 GB 版本的 eMMC 实际可寻址大小为 62,545,461,248 字节,但原厂分区并没有占满整个空间。安装器计算得到:
| 项目 | 本机实测值 |
|---|---|
| Linux 区域起点 | 27,762,098,176 字节 |
| Linux 文件系统容量 | 34,779,168,768 字节,约 32.39 GiB |
| Linux 区域结束位置,不含末字节 | 62,541,266,944 字节 |
这个区域位于现有分区之后、磁盘尾部 GPT 之前。安装器抽查的 16 个位置都没有发现已有数据,因此可以直接在这里建立文件系统,无须缩小 userdata,也无须修改 GPT。
项目提供 --check 用于检查安装条件,不过在本次固定版本中,入口仍然会经过 USB 模式处理等辅助逻辑。为了保留当前稳定的 USB 配置,并跳过已经确认无需执行的启动链同步,本次检查使用了:1
2
3MU300_USB_ADB_ONLY=0 \
MU300_SYNC_BOOTCHAIN=0 \
./install.sh --check --no-resume
这里的 MU300_SYNC_BOOTCHAIN=0 有前提:前面已经独立确认那五组 A/B 启动链一致。如果自己的设备不一致,应当先查明原因,不要为了跳过检查就照抄这个变量。
安装带 LuCI 的 OpenWrt
确认设备和存储布局后,安装选项如下:
| 安装选项 | 本次选择 |
|---|---|
| 存储 | 内部 eMMC 未分配区域 |
| 系统 | OpenWrt,带 LuCI |
| 内核 | 5.4 |
| 默认启动 | Linux |
| 连续启动失败后的回退次数 | 5 次 |
| 热点设置 | 继承 Android 的热点名称和密码 |
| Linux ADB | 只允许这台 Mac 的 ADB 公钥 |
| GPU、VPN 扩展 | 不安装 |
| 管理员密码 | 新设置独立密码 |
对应的主要下载文件是 mu300-kernel.tar.gz、mu300-openwrt-luci-rootfs.tar.gz 和 SHA256SUMS。本次先在 Mac 上下载并校验文件,再通过 USB 继续安装。
该版本的交互式安装入口如下;本次实际用答案文件提供了上表中的选项,并在固定版本上补充了下文的读回校验:1
2
3MU300_USB_ADB_ONLY=0 \
MU300_SYNC_BOOTCHAIN=0 \
./install.sh --no-resume
这里还有一个容易看反的选项:MU300_ANS_ADB=yes 表示限制为这台电脑的 ADB 公钥。它不是代表「是否开启 ADB」;这个版本中选择 no,反而表示任何连接 USB 的电脑都可以进入 Linux root ADB。
安装器会创建 Linux 文件系统、放入 OpenWrt、构建 Linux boot 镜像,然后写入 B 槽并更新启动控制记录。最终布局是:1
2
3
4boot_a B09 Android + Magisk
boot_b Linux 内核与 initramfs
eMMC 未分配尾部 OpenWrt ext4 文件系统
misc A/B 启动选择及失败回退信息
考虑到此前在 FDL 中遇到过成对写入,笔者在本次安装器中补充了完整校验:写入前保存 A/B boot 和完整 misc;写入 B 后,检查 A 的完整哈希没有变化,B 的镜像及剩余内容与预期一致;这些检查通过后才修改启动控制记录。安装最后,在重启前再次核对 A、B 和 misc。
实际检查全部通过。也就是说,此时原厂 Android A 槽确实被保留下来,Linux 写入的是已经确认独立的 B 槽块设备。
第一次启动与访问验证
安装结束后,设备自动重启。Mac 最先看到的 USB 产品名变成了:1
MU300 Linux USB Ethernet
但 USB 产品名出现时,网卡、ADB 和串口接口并没有立刻全部可用。这一阶段笔者先继续观察,随后 Mac 才枚举出 USB 网卡,ADB 中也出现了以 MU300LINUX- 开头的新设备。Linux 的 ADB 序列号和原厂 Android 不同,不能一直沿用原来的设备选择参数。
在只连接这台 ADB 设备的情况下,可以用下面的命令核对实际运行的系统:1
2
3
4
5
6
7adb devices
adb shell id -u
adb shell cat /proc/1/comm
adb shell uname -r
adb shell cat /etc/openwrt_release
adb shell cat /etc/mu300/image-version
adb shell cat /run/mu300/linux-slot
本次对应的关键结果为:1
2
3
4
5
6uid: 0
PID 1: procd
kernel: 5.4.254-gb50db5b6224c
OpenWrt: 25.12.6
image-version: v2026.11.02.1
linux-slot: b
此外,还检查了根文件系统对应的 loop 设备偏移、ext4 容量,以及 GPT 和启动链。确认 OpenWrt 使用的是前面规划的未分配空间,Android A 槽内容仍然一致。
切换到 6.18 主线内核
继续尝试 USB 网卡时,笔者在 MU300 的设备管理页面遇到了一个问题:「当前角色」显示「不可用」,切换 USB 角色的下拉框中只有「设备模式」可以选择,「主机模式」是灰色的。
检查这个版本的后台脚本后发现,它固定读取下面的文件:1
/sys/class/usb_role/25100000.dwc3-role-switch/role
读取失败时返回 role=unavailable;文件不存在时,再返回 host_supported=0,界面据此禁用 Host 选项。
实机检查与源码一致:5.4 内核下有对应的角色目录,却没有 role 文件。厂商内核没有为这个接口启用用户空间访问,USB 角色主要由 Type-C 检测和厂商驱动处理。因此,这个灰色按钮反映的是当前内核没有提供页面使用的手动控制接口。相关逻辑可以在 device-usb 中看到,项目的主线 6.18 则带有开放角色控制的补丁。
项目提供了在设备上原地更换内核的工具 mu300-update,可以升级到 Linux 6.18.60。这次仍然使用 v2026.11.02.1,只切换内核、配套模块和启动镜像,保留已经安装的 OpenWrt。
换内核前,笔者再次备份了完整的 A/B boot、misc 和 GPT,并把现有 OpenWrt 目录打包保存到 Mac 和设备上。这份归档包含配置、认证文件和原来的 5.4 模块。更新器自身也会保存上一份启动镜像,但独立备份仍然保留,方便新内核完全无法启动时恢复。
如果 F50 本身能够访问 GitHub,在它的 root shell 中可以直接执行:1
MU300_RELEASE=v2026.11.02.1 /opt/mu300/bin/mu300-update kernel 6.18
本次采用的是 Mac 下载、USB 上传的方式。下面的下载命令在 Mac 上执行:1
2
3
4
5
6
7
8
9mkdir -p f50-kernel-6.18
cd f50-kernel-6.18
release_url=https://github.com/dikeckaan/mu300-linux/releases/download/v2026.11.02.1
for file in mu300-kernel-6.18.tar.gz SHA256SUMS SHA256SUMS.sig; do
curl -fL "$release_url/$file" -o "$file" || break
done
shasum -a 256 mu300-kernel-6.18.tar.gz
本次内核包大小为 53,175,572 字节,SHA256 为:1
64893b185c5717b3c40699ab4863fb1f291a0e03eb869d50fdcf05a78014e130
确认三个文件均下载完成、哈希匹配后,通过 ADB 上传。以下以设备上的 kernel-switch 目录为例;不要把包放进更新器会自行清理的 .mu300-update 目录:1
2
3
4adb shell 'mkdir -p /mnt/mu300-disk/kernel-switch && chmod 700 /mnt/mu300-disk/kernel-switch'
adb push mu300-kernel-6.18.tar.gz SHA256SUMS SHA256SUMS.sig \
/mnt/mu300-disk/kernel-switch/
adb shell
进入 F50 的 root shell 后,使用已经安装的项目公钥验证签名,再按签名覆盖的清单核对版本和设备端内核包:1
2
3
4
5
6
7
8
9
10
11(
set -eu
cd /mnt/mu300-disk/kernel-switch
usign -V -p /opt/mu300/lib/release.pub \
-m SHA256SUMS -x SHA256SUMS.sig
printf '%s\n' v2026.11.02.1 > mu300-release
awk '$2 == "mu300-release" || $2 == "mu300-kernel-6.18.tar.gz"' \
SHA256SUMS > kernel-check.sha256
test "$(wc -l < kernel-check.sha256)" -eq 2
sha256sum -c kernel-check.sha256
)
只有签名验证通过、两个文件都校验为 OK 后才继续。其中 mu300-release 是版本号加换行,核对它的哈希可以让签名与这个发布版本对应起来。本次签名、版本绑定和上传前后的包哈希检查均通过。指定本地 MU300_KERNEL_BUNDLE 会跳过更新器的在线下载校验,因此这一步需要自己完成。
确认签名、版本和哈希后,在 F50 上执行:1
2
3MU300_RELEASE=v2026.11.02.1 \
MU300_KERNEL_BUNDLE=/mnt/mu300-disk/kernel-switch/mu300-kernel-6.18.tar.gz \
/opt/mu300/bin/mu300-update kernel 6.18
这条命令不会自动重启。它安装 6.18.60 的配套模块,保留设备专属的启动内容,重建 Linux B 槽的启动镜像,并保存旧镜像供 rollback-boot 使用。此时实际运行的仍是 5.4。
更新器报告成功后,笔者又独立读回 B 槽,检查新内核、ramdisk、vbmeta 和分区尾部;确认 A 槽和 GPT 没变,misc 中启动控制记录之外的内容没变,Android 回退与有限启动尝试次数仍然有效。原配置和旧模块也与备份匹配。日志区仍然按前文的 48–56 MiB 范围单独处理,没有把运行日志变化误当成刷写错误。
这些检查通过后,才在 F50 上正常重启:1
/sbin/reboot
这里又遇到一个小区别:对这次安装的 Linux 执行 adb reboot 时,客户端返回 error: closed,但设备的启动 ID 和运行时间没有重置,实际上并未重启。改用 OpenWrt 自身的 /sbin/reboot 后才成功。
重新连接后,uname -r 返回 6.18.60。SSH、LuCI 密码登录、Wi-Fi 热点和基带相关服务检查通过;系统配置、热点设置和认证文件的哈希保持不变,Android A 槽也没有变化。
如果新内核能够进入 Linux,但使用中发现问题,可以在 F50 上恢复更新器保留的旧启动镜像及相关模块:1
/opt/mu300/bin/mu300-update rollback-boot
等它报告成功后,再执行 /sbin/reboot。如果新内核完全无法启动,连续失败触发的是回到 Android,不会自动把 Linux 内核恢复成 5.4;这时需要利用 Android 和电脑端恢复路径处理。上游也记录过 6.18 的早期启动挂起问题,因此仍应保留旧内核备份,在出错时进行恢复。
开启 USB Host 与接入有线网卡
切换到 6.18.60 后,再在 Mac 上读取 USB 状态:1
adb shell /usr/libexec/unisoc-modem/device-usb get
关键字段变成了:1
2
3
4
5{
"role": "device",
"host_supported": 1,
"role_auto": 0
}
此时 role 文件已经存在,而且可以读写,LuCI 据此启用「主机模式」选项。
F50 只有一个 Type-C 口,切入 Host 后,这个口上的 USB 网络、ADB 等管理连接会断开。因此,应当先通过 F50 的 Wi-Fi 建立可用的管理连接,并准备能够让 F50 持续受电、同时作为 USB 主机连接网卡的转接方案。普通带电源的 Hub 不一定会从上行口给 F50 供电,换了内核也不能保证任意 PD 扩展坞都能使用。所以,必须挑选好适合的拓展坞或网卡设备。
在 MU300 的设备管理页面中,找到 USB 角色设置,选择「主机模式」,先不要勾选「开机启用主机模式」,再应用。稍后通过 Wi-Fi 刷新页面,确认当前角色实际变成 Host。对应的命令是在 F50 的 root shell 中执行:1
/usr/libexec/unisoc-modem/device-usb set-role host 0
最后的 0 表示不设置开机自动 Host。命令可能先返回 pending=1,这只说明切换请求已接受;稍后还需要再次执行 device-usb get,确认 role 已经是 host。
接上兼容的 USB 网卡后,可以在页面刷新网卡列表。命令行对应:1
/usr/libexec/unisoc-modem/device-usb net-list
这个操作会尝试将发现的可用网卡置为 up,并列出实际接口名。如果仍处于 Device 模式,它会返回空列表和 host_only=1。
如果打算用网口向电脑或另一台路由器提供 F50 的网络,可以将识别到的网卡「加入 LAN」。例如,当上一步实际识别出的接口名是 eth1 时,命令是:1
/usr/libexec/unisoc-modem/device-usb net-add eth1
这一步会保存 LAN 桥接配置并重载网络。
等到网卡识别、下游获取地址、持续供电和重启恢复都确认正常后,再考虑勾选「开机启用主机模式」。对应命令为:1
/usr/libexec/unisoc-modem/device-usb set-role host 1
它会立即切换,并保存开机自动 Host 策略,同时关闭 USB gadget 网络协议的自动应用策略。若需要恢复 Device 模式并取消自动 Host,应通过仍然可用的 Wi-Fi 管理连接执行:1
/usr/libexec/unisoc-modem/device-usb set-role device 0
这里的 USB 角色与 NCM、ECM、RNDIS 是两回事:后面三者是 F50 作为 USB 设备连接电脑时使用的网络功能,外接 USB 网卡则要求 F50 作为主机。相关行为可参阅项目的 USB 设备管理说明。
重启与切回 Android
本次选择了默认启动 Linux。可以在 OpenWrt 的 SSH 中查看:1
/opt/mu300/bin/mu300-next-boot status
实际状态包含:1
2
3
4default boot: linux
locked: no
failed boots in a row before Android: 5
boots since the last good one: 0
因此,正常软件重启或断电重插都会继续进入 OpenWrt。这里的 5 次指连续启动失败的回退阈值,正常成功启动会清零。
如果需要切回 Android,项目提供的命令是:1
/opt/mu300/bin/mu300-next-boot android && sync && reboot
这个操作同时把默认系统改回 Android,Linux 文件系统仍然保留。在已 root 的 Android 中,可以通过安装器放入的 Magisk 命令重新进入 Linux:1
adb shell su -c mu300-linux
如果之前已经把默认启动改为 Android,进入 Linux 后需要恢复默认 Linux,还可以执行:1
/opt/mu300/bin/mu300-next-boot linux
参考项目与源码: