OpenWrt新机的固件编译/定制
OpenWrt新机的固件编译/定制
背景
手里有一台QCA的IPQ4019的路由器,为TP-Link的Deco M9 Plus 2.0。
查了下OpenWrt已经支持这个CPU了,便打算刷机。
关于如何搭建OpenWrt的编译环境,请见:
OpenWrt编译环境搭建 - 付时凡 - 博客园 (cnblogs.com)
硬件参数
已知参数:
硬件参数 | 型号/规格 | 说明 |
---|---|---|
CPU | IPQ4019 | |
Flash | Nand Flash(128M) | Winbond W29N01HV |
RAM | 512M | |
PHY | QCA8072 | |
无线 | 2.4G+5G band1:IPQ4019内置;5G4:QCA9886 | 规格AC2200 |
USB 2.0 | 1个 | |
蓝牙 | CSR8811 | UART总线 |
Zigbee | EFR32MG | SPI总线 |
软件修改
新增profile
工作目录应该在openwrt/target/linux/ipq40xx
。所以:
cd openwrt/target/linux/ipq40xx
目前软件为Deco M9 Plus V2,结合之前以后的Deco系列命名:
$ rg deco
base-files/etc/board.d/02_network
129: tplink,deco-m4r-v4)
base-files/etc/board.d/01_leds
201:tplink,deco-m4r-v4)
应该定为tplink,deco-m9plus-v2
。
然后参考其他新机的提交,应该在以下文件修改:
$ rg ac2200
image/generic.mk
193:define Device/asus_map-ac2200
200:TARGET_DEVICES += asus_map-ac2200
1136:TARGET_DEVICES += asus_map-ac2200
files/arch/arm/boot/dts/qcom-ipq4019-map-ac2200.dts
11: compatible = "asus,map-ac2200";
base-files/etc/uci-defaults/04_led_migration
6:asus,map-ac2200)
base-files/etc/hotplug.d/firmware/11-ath10k-caldata
21: asus,map-ac2200)
56: asus,map-ac2200|\
153: asus,map-ac2200|\
base-files/etc/board.d/02_network
34: asus,map-ac2200|\
base-files/lib/preinit/05_set_iface_mac_ipq40xx.sh
5: asus,map-ac2200)
base-files/lib/upgrade/platform.sh
147: asus,map-ac2200)
参考修改即可:
diff --git a/target/linux/ipq40xx/base-files/etc/board.d/02_network b/target/linux/ipq40xx/base-files/etc/board.d/02_network
index babde1b..70a4a3d 100644
--- a/target/linux/ipq40xx/base-files/etc/board.d/02_network
+++ b/target/linux/ipq40xx/base-files/etc/board.d/02_network
@@ -32,6 +32,7 @@ ipq40xx_setup_interfaces()
8dev,jalapeno|\
alfa-network,ap120c-ac|\
asus,map-ac2200|\
+ tplink,deco-m9plus-v2|\
cilab,meshpoint-one|\
edgecore,ecw5211|\
glinet,gl-ap1300|\
diff --git a/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
index 654be26..2bbdf96 100644
--- a/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
+++ b/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
@@ -18,6 +18,7 @@ case "$FIRMWARE" in
;;
"ath10k/pre-cal-pci-0000:01:00.0.bin")
case "$board" in
+ tplink,deco-m9plus-v2|\
asus,map-ac2200)
caldata_extract_ubi "Factory" 0x9000 0x2f20
ln -sf /lib/firmware/ath10k/pre-cal-pci-0000\:00\:00.0.bin \
@@ -54,6 +55,7 @@ case "$FIRMWARE" in
"ath10k/pre-cal-ahb-a000000.wifi.bin")
case "$board" in
asus,map-ac2200|\
+ tplink,deco-m9plus-v2|\
asus,rt-ac42u|\
asus,rt-ac58u)
caldata_extract_ubi "Factory" 0x1000 0x2f20
@@ -151,6 +153,7 @@ case "$FIRMWARE" in
"ath10k/pre-cal-ahb-a800000.wifi.bin")
case "$board" in
asus,map-ac2200|\
+ tplink,deco-m9plus-v2|\
asus,rt-ac58u)
caldata_extract_ubi "Factory" 0x5000 0x2f20
;;
diff --git a/target/linux/ipq40xx/base-files/lib/preinit/05_set_iface_mac_ipq40xx.sh b/target/linux/ipq40xx/base-files/lib/preinit/05_set_iface_mac_ipq40xx.sh
index 96e70f6..a93c646 100644
--- a/target/linux/ipq40xx/base-files/lib/preinit/05_set_iface_mac_ipq40xx.sh
+++ b/target/linux/ipq40xx/base-files/lib/preinit/05_set_iface_mac_ipq40xx.sh
@@ -2,6 +2,11 @@
preinit_set_mac_address() {
case $(board_name) in
+ tplink,deco-m9plus-v2)
+ base_mac=$(mtd_get_mac_binary_ubi Factory 0x1006)
+ ip link set dev eth1 address $(macaddr_add "$base_mac" 1)
+ ip link set dev eth2 address $(macaddr_add "$base_mac" 3)
+ ;;
asus,map-ac2200)
base_mac=$(mtd_get_mac_binary_ubi Factory 0x1006)
ip link set dev eth0 address $(macaddr_add "$base_mac" 1)
diff --git a/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh b/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
index 22f2ee9..5f1aa74 100644
--- a/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
+++ b/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
@@ -144,6 +144,7 @@ platform_do_upgrade() {
fi
nand_do_upgrade "$1"
;;
+ tplink,deco-m9plus-v2 |\
asus,map-ac2200)
CI_KERNPART="linux"
nand_do_upgrade "$1"
diff --git a/target/linux/ipq40xx/image/generic.mk b/target/linux/ipq40xx/image/generic.mk
index f5132cb..aa35b2f 100644
--- a/target/linux/ipq40xx/image/generic.mk
+++ b/target/linux/ipq40xx/image/generic.mk
@@ -1109,6 +1109,23 @@ endef
# Missing DSA Setup
#TARGET_DEVICES += tel_x1pro
+define Device/tplink_deco-m9plus-v2
+ $(call Device/FitzImage)
+ $(call Device/UbiFit)
+ DEVICE_VENDOR := TP-Link
+ DEVICE_MODEL := M9 Plus
+ DEVICE_VARIANT := v2
+ SOC := qcom-ipq4019
+ DEVICE_DTS_CONFIG := config@ap.dk07.1-c1
+ KERNEL_INITRAMFS_SUFFIX := -recovery.itb
+ IMAGE/factory.ubi := append-ubi
+ PAGESIZE := 2048
+ BLOCKSIZE := 128k
+ KERNEL_SIZE := 4096k
+ IMAGE_SIZE := 31232k
+ KERNEL_IN_UBI := 1
+ DEVICE_PACKAGES := ath10k-firmware-qca9888-ct \
+ kmod-fs-ext4 kmod-mmc kmod-spi-dev mkf2fs e2fsprogs kmod-fs-f2fs
+endef
+TARGET_DEVICES += tplink_deco-m9plus-v2
define Device/unielec_u4019-32m
$(call Device/FitImage)
DEVICE_VENDOR := Unielec
其中,DEVICE_DTS_CONFIG这个参数,需要结合M9Plus的启动参数来判断,日志会输出这个信息。
最关键的文件是dts文件,即设备树。
设备树
因为都是ipq40xx
,所以很多和同个目录下的机型,是可以共用的,即头部包含的dtsi
文件。
但特别需要关注几个硬件参数:
- 内存
- Flash大小/分区表
- 无线接口
- 有线接口
- GPIO(LED+Reset)
内存
相对简单,基本上找个512M机型的dts文件照抄即可;这里参考的是GL的B2000:
memory {
device_type = "memory";
reg = <0x80000000 0x10000000>;
};
Flash/分区表
首先需要知道原有的分区,根据启动日志:
name = SBL1 , base = 0x00000000, size = 0x00030000 Bytes, usedFlag = 1
name = boot-config_0 , base = 0x00030000, size = 0x00010000 Bytes, usedFlag = 1
name = MIBIB , base = 0x00040000, size = 0x00010000 Bytes, usedFlag = 1
name = boot-config_1 , base = 0x00050000, size = 0x00010000 Bytes, usedFlag = 1
name = QSEE , base = 0x00060000, size = 0x00060000 Bytes, usedFlag = 1
name = CDT , base = 0x000c0000, size = 0x00010000 Bytes, usedFlag = 1
name = DDRPARAMS , base = 0x000d0000, size = 0x00010000 Bytes, usedFlag = 1
name = uboot-env , base = 0x000e0000, size = 0x00010000 Bytes, usedFlag = 1
name = fs-uboot@0 , base = 0x000f0000, size = 0x00080000 Bytes, usedFlag = 1
name = radio , base = 0x00170000, size = 0x0000fff0 Bytes, usedFlag = 1
name = bluetooth-XTAL , base = 0x0017fff0, size = 0x00000010 Bytes, usedFlag = 1
name = default-mac , base = 0x00180000, size = 0x00001000 Bytes, usedFlag = 1
name = device-id , base = 0x00182000, size = 0x00001000 Bytes, usedFlag = 1
name = product-info , base = 0x00183000, size = 0x00005000 Bytes, usedFlag = 1
name = support-list , base = 0x00190000, size = 0x00010000 Bytes, usedFlag = 1
name = group-info , base = 0x001a0000, size = 0x00010000 Bytes, usedFlag = 1
name = user-config , base = 0x001b0000, size = 0x00010000 Bytes, usedFlag = 1
name = device-config , base = 0x001c0000, size = 0x00100000 Bytes, usedFlag = 1
name = partition-table@0 , base = 0x002c0000, size = 0x00002000 Bytes, usedFlag = 1
name = os-image@0 , base = 0x002d0000, size = 0x00010000 Bytes, usedFlag = 1
name = file-system@0 , base = 0x002e0000, size = 0x00010000 Bytes, usedFlag = 1
name = soft-version@0 , base = 0x002f0000, size = 0x00010000 Bytes, usedFlag = 1
name = profile@0 , base = 0x00300000, size = 0x00010000 Bytes, usedFlag = 1
name = default-config@0 , base = 0x00310000, size = 0x00010000 Bytes, usedFlag = 1
name = partition-table , base = 0x00320000, size = 0x00002000 Bytes, usedFlag = 1
name = fs-uboot , base = 0x00330000, size = 0x00080000 Bytes, usedFlag = 1
name = os-image , base = 0x003b0000, size = 0x00010000 Bytes, usedFlag = 1
name = file-system , base = 0x003c0000, size = 0x00010000 Bytes, usedFlag = 1
name = soft-version , base = 0x003d0000, size = 0x00010000 Bytes, usedFlag = 1
name = profile , base = 0x003e0000, size = 0x00010000 Bytes, usedFlag = 1
name = default-config , base = 0x003f0000, size = 0x00010000 Bytes, usedFlag = 1
name = tm-sig , base = 0x00400000, size = 0x00200000 Bytes, usedFlag = 1
name = user-config-bak , base = 0x00600000, size = 0x00010000 Bytes, usedFlag = 1
name = zigbee-cal , base = 0x00610000, size = 0x00000010 Bytes, usedFlag = 1
name = zigbee-firmware , base = 0x02700000, size = 0x00040000 Bytes, usedFlag = 1
name = emmc-gpt , base = 0x04000000, size = 0x00004400 Bytes, usedFlag = 1
name = emmc-os@0 , base = 0x04004400, size = 0x00400000 Bytes, usedFlag = 1
name = emmc-files@0 , base = 0x04404400, size = 0x04000000 Bytes, usedFlag = 1
name = emmc-os , base = 0x08404400, size = 0x00400000 Bytes, usedFlag = 1
name = emmc-files , base = 0x08804400, size = 0x04000000 Bytes, usedFlag = 1
name = emmc-config , base = 0x0c804400, size = 0x04000000 Bytes, usedFlag = 1
可以根据自己的需求来调整。
通常情况下,不修改uboot之前的分区。
无线接口
根据同目录下其他 AC200规格的写法,这里参考的是GL.iNet GL-B2200
:
&wifi0 {
status = "okay";
nvmem-cell-names = "pre-calibration";
nvmem-cells = <&precal_art_1000>;
qcom,ath10k-calibration-variant = "GL-B2200";
};
&wifi1 {
status = "okay";
nvmem-cell-names = "pre-calibration";
nvmem-cells = <&precal_art_5000>;
qcom,ath10k-calibration-variant = "GL-B2200";
ieee80211-freq-limit = <5100000 5400000>;
};
&pcie0 {
status = "okay";
perst-gpio = <&tlmm 38 GPIO_ACTIVE_LOW>;
wake-gpio = <&tlmm 50 GPIO_ACTIVE_LOW>;
bridge@0,0 {
reg = <0x00000000 0 0 0 0>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
wifi2: wifi@1,0 {
status = "okay";
/* Bootlog shows this is a 168c:0056 - QCA 9888v2 */
compatible = "qcom,ath10k";
reg = <0x00010000 0 0 0 0>;
nvmem-cell-names = "pre-calibration";
nvmem-cells = <&precal_art_9000>;
qcom,ath10k-calibration-variant = "GL-B2200";
ieee80211-freq-limit = <5450000 5900000>;
};
};
};
有线接口
&swport4 {
status = "okay";
label = "eth2";
nvmem-cell-names = "mac-address";
nvmem-cells = <&macaddr_gmac1>;
};
&swport5 {
status = "okay";
label = "eth1";
nvmem-cell-names = "mac-address";
nvmem-cells = <&macaddr_gmac0>;
};
注意修改对应的MAC地址。
GPIO
两类:
-
LED
leds { compatible = "gpio-leds"; power_red: red { label = "red:system"; gpios = <&tlmm 0 GPIO_ACTIVE_LOW>; }; power_green: green { label = "green:system"; gpios = <&tlmm 1 GPIO_ACTIVE_LOW>; }; power_blue: blue { label = "blue:system"; gpios = <&tlmm 2 GPIO_ACTIVE_LOW>; }; };
-
Reset Button
keys { compatible = "gpio-keys"; reset { label = "reset"; gpios = <&tlmm 18 GPIO_ACTIVE_LOW>; linux,code = <KEY_RESTART>; linux,input-type = <1>; }; };
完整汇总如下:
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved.
* Copyright (c) 2020 Yanase Yuki <dev@zpc.sakura.ne.jp>
*/
#include "qcom-ipq4019.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/soc/qcom,tcsr.h>
#include <dt-bindings/leds/common.h>
/ {
model = "TP-Link Deco M9Plus v2";
compatible = "tplink,deco-m9plus-v2", "qcom,ipq4019";
memory {
device_type = "memory";
reg = <0x80000000 0x20000000>;
};
chosen {
/*
* U-Boot adds "ubi.mtd=rootfs root=mtd:ubi_rootfs" to
* kernel command line. But we use different partition names,
* so we have to set correct parameters.
*/
bootargs-append = " clk_ignore_unused";
stdout-path = &blsp1_uart1;
};
aliases {
ethernet1 = &swport4;
led-boot = &power_red;
led-failsafe = &power_red;
led-running = &power_green;
led-upgrade = &power_green;
};
soc {
rng@22000 {
status = "okay";
};
tcsr@1949000 {
compatible = "qcom,tcsr";
reg = <0x1949000 0x100>;
qcom,wifi_glb_cfg = <TCSR_WIFI_GLB_CFG>;
};
tcsr@194b000 {
compatible = "qcom,tcsr";
reg = <0x194b000 0x100>;
qcom,usb-hsphy-mode-select = <TCSR_USB_HSPHY_HOST_MODE>;
status = "okay";
};
ess_tcsr@1953000 {
compatible = "qcom,tcsr";
reg = <0x1953000 0x1000>;
qcom,ess-interface-select = <TCSR_ESS_PSGMII>;
};
tcsr@1957000 {
compatible = "qcom,tcsr";
reg = <0x1957000 0x100>;
qcom,wifi_noc_memtype_m0_m2 = <TCSR_WIFI_NOC_MEMTYPE_M0_M2>;
};
crypto@8e3a000 {
status = "okay";
};
watchdog@b017000 {
status = "okay";
};
};
leds {
compatible = "gpio-leds";
power_red: red {
label = "red:system";
gpios = <&tlmm 0 GPIO_ACTIVE_LOW>;
};
power_green: green {
label = "green:system";
gpios = <&tlmm 1 GPIO_ACTIVE_LOW>;
};
power_blue: blue {
label = "blue:system";
gpios = <&tlmm 2 GPIO_ACTIVE_LOW>;
};
};
keys {
compatible = "gpio-keys";
reset {
label = "reset";
gpios = <&tlmm 18 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
linux,input-type = <1>;
};
};
};
&tlmm {
serial_0_pins: serial0_pinmux {
mux {
pins = "gpio16", "gpio17";
function = "blsp_uart0";
bias-disable;
};
};
mdio_pins: mdio_pinmux {
mux_1 {
pins = "gpio6";
function = "mdio";
bias-pull-up;
};
mux_2 {
pins = "gpio7";
function = "mdc";
bias-pull-up;
};
};
};
&blsp_dma {
status = "okay";
};
&blsp1_uart1 {
pinctrl-0 = <&serial_0_pins>;
pinctrl-names = "default";
status = "okay";
};
&cryptobam {
status = "okay";
};
&qpic_bam {
status = "okay";
};
&nand {
status = "okay";
nand@0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "sbl1";
reg = <0x0 0x100000>;
read-only;
};
partition@100000 {
label = "mibib";
reg = <0x100000 0x100000>;
read-only;
};
partition@200000 {
label = "boot-config-0";
reg = <0x200000 0x100000>;
read-only;
};
partition@300000 {
label = "qsee";
reg = <0x300000 0x100000>;
read-only;
};
partition@400000 {
label = "cdt";
reg = <0x400000 0x80000>;
read-only;
};
partition@480000 {
label = "boot-config-1";
reg = <0x480000 0x80000>;
read-only;
};
partition@500000 {
label = "uboot-env";
reg = <0x500000 0x180000>;
read-only;
};
partition@680000 {
label = "uboot";
reg = <0x680000 0x200000>;
read-only;
};
partition@880000 {
label = "art";
reg = <0x880000 0x80000>;
read-only;
compatible = "nvmem-cells";
#address-cells = <1>;
#size-cells = <1>;
precal_art_1000: precal@1000 {
reg = <0x1000 0x2f20>;
};
precal_art_5000: precal@5000 {
reg = <0x5000 0x2f20>;
};
precal_art_9000: precal@9000 {
reg = <0x9000 0x2f20>;
};
macaddr_gmac0: macaddr@0 {
reg = <0x0 0x6>;
};
macaddr_gmac1: macaddr@6 {
reg = <0x6 0x6>;
};
};
partition@900000 {
label = "ubi";
reg = <0x900000 0x7680000>;
};
};
};
};
&gmac {
status = "okay";
};
&switch {
status = "okay";
};
&swport4 {
status = "okay";
label = "eth2";
nvmem-cell-names = "mac-address";
nvmem-cells = <&macaddr_gmac1>;
};
&swport5 {
status = "okay";
label = "eth1";
nvmem-cell-names = "mac-address";
nvmem-cells = <&macaddr_gmac0>;
};
&wifi0 {
status = "okay";
ieee80211-freq-limit = <2401000 2473000>;
qcom,ath10k-calibration-variant = "TP-Link Deco M9Plus v2";
nvmem-cell-names = "pre-calibration", "mac-address";
nvmem-cells = <&precal_art_1000>;
mac-address-increment = <1>;
};
&wifi1 {
status = "okay";
ieee80211-freq-limit = <5170000 5250000>;
qcom,ath10k-calibration-variant = "TP-Link Deco M9Plus v2";
nvmem-cell-names = "pre-calibration", "mac-address";
nvmem-cells = <&precal_art_5000>;
mac-address-increment = <2>;
};
&pcie0 {
status = "okay";
perst-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
wake-gpios = <&tlmm 40 GPIO_ACTIVE_LOW>;
clkreq-gpios = <&tlmm 39 GPIO_ACTIVE_LOW>;
bridge@0,0 {
reg = <0x00000000 0 0 0 0>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
wifi2: wifi@1,0 {
compatible = "qcom,ath10k";
reg = <0x00010000 0 0 0 0>;
ieee80211-freq-limit = <5450000 5900000>;
qcom,ath10k-calibration-variant = "TP-Link Deco M9Plus v2";
nvmem-cell-names = "pre-calibration", "mac-address";
nvmem-cells = <&precal_art_9000>;
};
};
};
&mdio {
status = "okay";
pinctrl-0 = <&mdio_pins>;
pinctrl-names = "default";
reset-gpios = <&tlmm 41 GPIO_ACTIVE_LOW>;
};
&usb2 {
status = "okay";
dwc3@6000000 {
#address-cells = <1>;
#size-cells = <0>;
usb2_port1: port@1 {
reg = <1>;
#trigger-source-cells = <0>;
};
};
};
&usb2_hs_phy {
status = "okay";
};
&usb3 {
status = "okay";
dwc3@8a00000 {
#address-cells = <1>;
#size-cells = <0>;
usb3_port1: port@1 {
reg = <1>;
#trigger-source-cells = <0>;
};
usb3_port2: port@2 {
reg = <2>;
#trigger-source-cells = <0>;
};
};
};
&usb3_hs_phy {
status = "okay";
};
&usb3_ss_phy {
status = "okay";
};
至此,便可以通过:
make menuconfig
选中tplink,deco-m9plus-v2
机型,然后编译。
编译后的固件:
$ ls bin/targets/ipq40xx/generic/ |grep m9
openwrt-ipq40xx-generic-tplink_deco-m9plus-v2-initramfs-recovery.itb
openwrt-ipq40xx-generic-tplink_deco-m9plus-v2.manifest
openwrt-ipq40xx-generic-tplink_deco-m9plus-v2-squashfs-factory.ubi
openwrt-ipq40xx-generic-tplink_deco-m9plus-v2-squashfs-sysupgrade.bin
刷机
参考另一篇文章:
uboot相关指令 - 付时凡 - 博客园 (cnblogs.com)
这里只记录指令:
擦写root.ubi:
nand erase 0x900000 0x7680000 && setenv serverip 192.168.0.66 && setenv ipaddr 192.168.0.171 && tftpboot root.ubi
nand write 0x84000000 0x900000 0x1500000
re
改写bootcmd:
setenv bootargs 'ubi.mtd=ubi root=/dev/ubiblock0_1 rootfstype=squashfs,ubi'
setenv set_ubi 'set mtdids nand0=nand0; set mtdparts mtdparts=nand0:0x7680000@0x900000(fs); ubi part fs'
setenv bootkernel 'ubi read 0x84000000 kernel && bootm'
setenv my_boot 'run set_ubi; run bootkernel'
setenv bootcmd 'run my_boot'
问题修复
启动后QCA9886未正常启动,报错如下:
[ 11.918714] ath10k_pci 0000:01:00.0: Failed to find firmware-N.bin (N between 2 and 6) from ath10k/QCA9888/hw2.0: -12
应该是需要选中QCA 9886相关的package:
CONFIG_PACKAGE_ath10k-firmware-qca9888=y
CONFIG_PACKAGE_ath10k-board-qca9888=y
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix