目录
一、Linux USB架构
USB 是一种分层总线结构。 USB 设备与主机之间的数据传输由 USB 控制器控制。 Linux USB 驱动程序架构如下图所示。 Linux USB 主机驱动包括三部分: USB 主机控制器驱动、 USB 核心和 USB 设备驱动。
USB 主机控制器驱动在分层结构的最底层,直接与硬件交互。 USB 核心是整个 USB 主机驱动的核心,用于管理 USB 总线、 USB 总线设备和 USB 总线带宽;它为 USB 设备驱动程序提供接口,应用程序可以通过这些接口访问 USB 系统文件。
USB 设备驱动程序与应用程序交互并提供用于访问特定 USB 设备的接口。
其实这一部分的讲析,在你调试的4g模块文档中也有,在此,不再作过多描述。
以下是我配置4g的原理图:
二、内核配置
A.相关配置文件:
dts文件
configs/rockchip_defconfig
option.c
usb_wwan.c
qmi_wwan_q.c
B.驱动配置
1.dts配置
rk_modem: rk-modem {
compatible="4g-modem-platdata";
4G,vbat-gpio = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>; //4g_en
4G,power-gpio = <&gpio3 RK_PC7 GPIO_ACTIVE_LOW>; //4g_disabled
4G,reset-gpio = <&gpio3 RK_PC6 GPIO_ACTIVE_HIGH>;//4g_rst
pinctrl-0 = <&mobile_4g>;
pinctrl-names = "default";
status = "okay";
};
其实这部分配置貌似不需要配置的很详细,只配置一个4g的供电就行了。这个compatible用到的驱动lte_rm310.c,这部分其实并没有按照驱动认真的跑,这个默认的源文件还是有问题的。
2.添加pid/vid
为了识别模块,需将模块的 VID 和 PID 信息添加到[KERNEL]/drivers/usb/serial/option.c 文件中,对应的 VID 和 PID,如下所示:
static const struct usb_device_id option_ids[] = {
#if 1 //Added by Quectel
{ USB_DEVICE(0x2C7C, 0x0125) }, /* Quectel EC20 R2.0/EC20 R2.1/EC25/EG25-G/EM05 */
{ USB_DEVICE(0x2C7C, 0x0121) }, /* Quectel EC21/EG21-G */
{ USB_DEVICE(0x2C7C, 0x0191) }, /* Quectel EG91 */
{ USB_DEVICE(0x2C7C, 0x0195) }, /* Quectel EG95 */
{ USB_DEVICE(0x2C7C, 0x0306) }, /* Quectel EG06/EP06/EM06 */
{ USB_DEVICE(0x2C7C, 0x0512) }, /* Quectel EG12/EM12/EG18 */
{ USB_DEVICE(0x2C7C, 0x0296) }, /* Quectel BG96 */
{ USB_DEVICE(0x2C7C, 0x0700) }, /* Quectel BG95/BG77/BG600L-M3/BC69 */
{ USB_DEVICE(0x2C7C, 0x0435) }, /* Quectel AG35 */
{ USB_DEVICE(0x2C7C, 0x0415) }, /* Quectel AG15 */
{ USB_DEVICE(0x2C7C, 0x0452) }, /* Quectel AG520R */
{ USB_DEVICE(0x2C7C, 0x0455) }, /* Quectel AG550R */
{ USB_DEVICE(0x2C7C, 0x0620) }, /* Quectel EG20 */
{ USB_DEVICE(0x2C7C, 0x0800) }, /* Quectel RG500Q/RM500Q/RG510Q/RM510Q */
{ USB_DEVICE(0x2C7C, 0x6002) }, // ec200s/ec200m/ec200n/ec600n/ec600m/ec800m/ec800n
{ USB_DEVICE(0x2C7C, 0x6005) }, // Quectel EC200A
#endif
3.使用USBNET驱动
前面第二点中配置使模块的所有 USB 接口均绑定 USB 转串口 option 驱动程序,导致 USBNet 驱动程序接口无法工作。用户可以添加以下语句来防止 USBNet 驱动程序接口绑定 USB 转串口 option 驱动程序。
高于 2.6.30 的 Linux 内核版本,用户可以在[KERNEL]/drivers/usb/serial/option.c 文件中添加以下语句
static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) {
struct usb_wwan_intf_private *data;
……
#if 1 //Added by Quectel
if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
__u16 idProduct = le16_to_cpu(serial->dev->descriptor.idProduct);
struct usb_interface_descriptor *intf = &serial->interface->cur_altsetting->desc;
if (intf->bInterfaceClass != 0xFF || intf->bInterfaceSubClass == 0x42) {
//ECM, RNDIS, NCM, MBIM, ACM, UAC, ADB
return -ENODEV;
}
if ((idProduct&0xF000) == 0x0000) {
//MDM interface 4 is QMI
if (intf->bInterfaceNumber == 4 && intf->bNumEndpoints == 3
&& intf->bInterfaceSubClass == 0xFF &&
intf->bInterfaceProtocol == 0xFF)
return -ENODEV;
}
}
#endif
/* Store device id so we can use it during attach. */
usb_set_serial_data(serial, (void *)id);
return 0;
}
3.修改内核配置
用户需启用以下配置项。
CONFIG_USB_SERIAL
CONFIG_USB_SERIAL_WWAN
CONFIG_USB_SERIAL_OPTION
4.添加零包机制
根据 USB 协议的要求,通过添加如下语句在 bulk-out 传输过程中添加处理零包的机制:
高于 2.6.34 的 Linux 内核版本,需在[KERNEL]/drivers/usb/serial/usb_wwan.c 文件中添加以下语句
static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint,
int dir, void *ctx, char *buf, int len,void (*callback) (struct urb *))
{
……
usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev, endpoint) | dir,
buf, len, callback, ctx);
#if 1 //Added by Quectel for zero packet
if (dir == USB_DIR_OUT) {
struct usb_device_descriptor *desc = &serial->dev->descriptor;
if (desc->idVendor == cpu_to_le16(0x2C7C))
urb->transfer_flags |= URB_ZERO_PACKET;
}
#endif
return urb;
}
5.添加Reset-resume机制
部分 USB 主机控制器或 USB hub 在 MCU 进入 Suspend/Sleep(挂起/睡眠)模式时会发生掉电或复位,并且在 MCU 退出 Suspend/Sleep 模式后不能使模块恢复。需要通过添加以下语句来启用 reset-resume机制。
高于 3.4 的 Linux 内核版本,需在[KERNEL]/drivers/usb/serial/option.c 文件中添加以下语句。
static struct usb_serial_driver option_1port_device = {
……
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
.resume = usb_wwan_resume,
#if 1 //Added by Quectel
.reset_resume = usb_wwan_resume,
#endif
#endif
};
6.驱动qmi_wwan
当模块成功加载 QMI_WWA 驱动程序后,会创建一个网络设备和一个 QMI 设备节点。网络设备名称为“wwanX”, QMI 设备节点名称为“/dev/cdc-wdmX”。网络设备用于数据传输, QMI 设备节点用于 QMI消息交互。
移远模块使用 qmi_wwan 驱动, 需修改驱动程序源文件[KERNEL]/drivers/net/usb/qmi_wwan.c。为简化移植步骤,移远提供了 QMI_WWAN 驱动。 QMI_WWAN 驱动源文件 qmi_wwan_q.c 可与 qmi_wwan.c共存,且仅用于移远模块。移植时, 将 qmi_wwan_q.c 复制到[KERNEL]/drivers/net/usb/目录下(这部分的文件,我会上传到资源文件中,需要的可以去查看)
用户需先启用以下配置项:
CONFIG_USB_NET_DRIVERS
CONFIG_USB_USBNET
CONFIG_USB_NET_QMI_WWAN
CONFIG_USB_WDM
然后添加如下语句至[KERNEL]/drivers/net/usb/Makefile。
# must insert qmi_wwan_q.o before qmi_wwan.o
obj-${CONFIG_USB_NET_QMI_WWAN} += qmi_wwan_q.o
obj-${CONFIG_USB_NET_QMI_WWAN} += qmi_wwan.o
C.常见问题和内核log
1.如何检查设备中是否存在 USB 驱动
可通过检查/sys/bus/usb/drivers 的目录确认是否存在 USB 驱动程序。例如:
root@vz_k56_qipai:~# ls /sys/bus/usb/drivers
GobiNet cdc_wdm rndis_host usbfs
cdc_ether hub uas usbserial
cdc_mbim option usb usbserial_generic
cdc_ncm qmi_wwan_q usb-storage
若需要 USB 转串口 option 驱动程序,请确保/sys/bus/usb/drivers 目录下存在 option。 同理, 若需要GobiNet 驱动程序,请确保 GobiNet 存在。 若需要 QMI_WWAN 驱动程序,请确保 qmi_wwan_q 存在,依此类推。
2.如何检查模块与对应的 USB 驱动程序是否正常工作
对于 USB 转串口 option 和QMI_WWAN驱动:不同模块的内核 log 除了 VID&PID 信息(下图红框中所示信息)外几乎相同。
三、上层配置
进入目录device/rockchip/common
A.添加RIL
1.普通的添加方式应该是将供应商提供的so文件等置入以下路径:
rk3568\vendor\rockchip\common\phone\phone.mk
#########################################################
# 3G Dongle SUPPORT
#########################################################
-#PRODUCT_COPY_FILES += \
-# $(CUR_PATH)/phone/etc/ppp/ip-down:system/etc/ppp/ip-down \
-# $(CUR_PATH)/phone/etc/ppp/ip-up:system/etc/ppp/ip-up \
-# $(CUR_PATH)/phone/etc/ppp/call-pppd:system/etc/ppp/call-pppd \
-# $(CUR_PATH)/phone/etc/operator_table:system/etc/operator_table
+PRODUCT_COPY_FILES += \
+ $(CUR_PATH)/phone/etc/ppp/ip-down:system/etc/ppp/ip-down \
+ $(CUR_PATH)/phone/etc/ppp/ip-up:system/etc/ppp/ip-up \
+ $(CUR_PATH)/phone/bin/chat:system/bin/chat \
+ $(CUR_PATH)/phone/lib/libreference-ril-ec200m-cn.so:vendor/lib64/libreference-ril-ec200m-cn.so \
+ $(CUR_PATH)/phone/etc/ppp/ql-ril.conf:system/etc/ql-ril.conf \
+ $(CUR_PATH)/etc/spn-conf.xml:system/etc/spn-conf.xml
我这里为了方便适配多种4g模块,添加方式如下:
我在相应的产品路径下device/rockchip/rk356x,新建了一个4g文件夹,将so文件在编译时直接copy到了相应路径:
#device/rockchip/rk356x/xxx产品/xx.mk
#for 4g
PRODUCT_MODEM := EC200M-CN
ifeq ($(strip $(PRODUCT_MODEM)), EC20)
BOARD_HAS_RK_4G_MODEM := true
PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/../4g/ec20/arm64-v8a/libreference-ril-ec20.so:vendor/lib64/hw/libreference-ril.so
PRODUCT_PROPERTY_OVERRIDES += \
vendor.rild.libpath=/vendor/lib64/hw/libreference-ril.so
endif
ifeq ($(strip $(PRODUCT_MODEM)), EC200M-CN)
BOARD_HAS_RK_4G_MODEM := true
PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/../4g/ec200m-cn/RIL_64bits/libreference-ril-ec200m-cn.so:vendor/lib64/hw/libreference-ril.so
PRODUCT_PROPERTY_OVERRIDES += \
vendor.rild.libpath=/vendor/lib64/hw/libreference-ril.so
endif
2.设置属性(rild.libpath)
RK3568\device\rockchip\rk356x\device.mk
PRODUCT_PROPERTY_OVERRIDES += \
ro.ril.ecclist=112,911 \
ro.opengles.version=196610 \
wifi.interface=wlan0 \
+ ro.telephony.default_network=9 \
+ rild.libpath=/vendor/lib64/hw/libreference-ril.so \
+ rild.libargs=-d /dev/ttyUSB2 \
ro.audio.monitorOrientation=true \
debug.nfc.fw_download=false \
debug.nfc.se=false \
vendor.hwc.compose_policy=1 \
sys.wallpaper.rgb565=0 \
sf.power.control=2073600 \
sys.rkadb.root=0 \
ro.sf.fakerotation=false \
ro.tether.denied=false \
sys.resolution.changed=false \
ro.default.size=100 \
ro.product.usbfactory=rockchip_usb \
wifi.supplicant_scan_interval=15 \
ro.factory.tool=0 \
ro.kernel.android.checkjni=0 \
ro.build.shutdown_timeout=6 \
persist.enable_task_snapshots=false \
ro.vendor.frameratelock=true
#
可以使用串口指令查看加载库是否正确
console:/ # getprop | grep rild
[rild.libargs]: [-d]
[rild.libpath]: [/vendor/lib64/hw/libreference-ril.so]
[vendor.rild.libpath]: [/vendor/lib64/hw/libreference-ril.so]
B.进入device/rockchip/common目录
1.关闭了selinux
2.屏蔽了RK默认copy的so库文件
diff --git a/BoardConfig.mk b/BoardConfig.mk
index b2c6551..1888ef9 100755
--- a/BoardConfig.mk
+++ b/BoardConfig.mk
@@ -59,7 +59,7 @@ BOARD_BOOT_HEADER_VERSION ?= 2
BOARD_MKBOOTIMG_ARGS :=
BOARD_PREBUILT_DTBOIMAGE ?= $(TARGET_DEVICE_DIR)/dtbo.img
BOARD_ROCKCHIP_VIRTUAL_AB_ENABLE ?= false
-BOARD_SELINUX_ENFORCING ?= true
+BOARD_SELINUX_ENFORCING ?= false
# Use the non-open-source parts, if they're present
TARGET_PREBUILT_KERNEL ?= kernel/arch/arm/boot/zImage
diff --git a/device.mk b/device.mk
index a3e0d59..e50be92 100644
--- a/device.mk
+++ b/device.mk
@@ -273,7 +273,7 @@ PRODUCT_PROPERTY_OVERRIDES += \
ro.telephony.default_network=9
ifeq ($(strip $(TARGET_ARCH)), arm64)
-PRODUCT_PROPERTY_OVERRIDES += \
+#PRODUCT_PROPERTY_OVERRIDES += \
vendor.rild.libpath=/vendor/lib64/librk-ril.so
PRODUCT_COPY_FILES += \
3.添加相应的hidl支持
Android手机框架与服务ril-daemon的通信接口由socket改为HIDL。
因此,需要将以下HIDL描述添加到manifest.xml中,部分是在4g_modem/maniest.xml已定义,需要定义这2者的冲突
diff --git a/manifest.xml b/manifest.xml
index 18ed438..097e105 100755
--- a/manifest.xml
+++ b/manifest.xml
@@ -65,7 +65,34 @@
<name>ISensors</name>
<instance>default</instance>
</interface>
+ </hal>
+
+<hal format="hidl">
+ <name>android.hardware.radio</name>
+ <transport>hwbinder</transport>
+ <fqname>@1.1::IRadio/slot1</fqname>
+ <fqname>@1.1::IRadio/slot2</fqname>
+ <fqname>@1.2::ISap/slot1</fqname>
+ </hal>
+ <hal format="hidl">
+ <name>android.hardware.radio.deprecated</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IOemHook</name>
+ <instance>slot1</instance>
+ </interface>
</hal>
+ <hal format="hidl">
+ <name>android.hardware.radio</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IRadio</name>
+ <instance>slot1</instance>
+ </interface>
+ </hal>
+
<kernel target-level="5"/>
</manifest>
4.添加权限
diff --git a/ueventd.rockchip.rc b/ueventd.rockchip.rc
index dc1086b..e19df8d 100755
--- a/ueventd.rockchip.rc
+++ b/ueventd.rockchip.rc
@@ -68,16 +68,19 @@
/dev/hidraw0 0660 audio audio
# for radio
-/dev/ttyUSB0 0660 radio radio
-/dev/ttyUSB1 0660 radio radio
-/dev/ttyUSB2 0660 radio radio
-/dev/ttyUSB3 0660 radio radio
-/dev/ttyUSB4 0660 radio radio
-/dev/ttyUSB5 0660 radio radio
-/dev/ttyUSB6 0660 radio radio
-/dev/ttyUSB7 0660 radio radio
-/dev/ttyUSB8 0660 radio radio
-/dev/ttyUSB9 0660 radio radio
+/dev/ttyUSB0 0666 radio radio
+/dev/ttyUSB1 0666 radio radio
+/dev/ttyUSB2 0666 radio radio
+/dev/ttyUSB3 0666 radio radio
+/dev/ttyUSB4 0666 radio radio
+/dev/ttyUSB5 0666 radio radio
+/dev/ttyUSB6 0666 radio radio
+/dev/ttyUSB7 0666 radio radio
+/dev/ttyUSB8 0666 radio radio
+/dev/ttyUSB9 0666 radio radio
+
+#for cdc-wdm0
+/dev/cdc-wdm* 0660 radio radio
# for mali-t764
/dev/mali0 0666 system system
5.为RIL配置SELinux规则
diff --git a/sepolicy/vendor/file_contexts b/sepolicy/vendor/file_contexts
index e9678d8..8c4f573 100644
--- a/sepolicy/vendor/file_contexts
+++ b/sepolicy/vendor/file_contexts
@@ -223,3 +223,12 @@
/vendor/bin/flash_img.sh u:object_r:vendor_install_recovery_exec:s0
# vibrator aidl
# vibrator aidl
# vibrator aidl
# vibrator aidl
# vibrator aidl
/(vendor|system/vendor)/bin/hw/android\.hardware\.vibrator-service\.rockchip u:object_r:hal_vibrato
r_default_exec:s0
+
+#for 4g
+/dev/ttyUSB[0-9] u:object_r:radio_device:s0
+/dev/ttyACM[0-9] u:object_r:radio_device:s0
+/dev/cdc-wdm[0-9] u:object_r:radio_device:s0
+/dev/qcqmi[0-9] u:object_r:radio_device:s0
+/vendor/bin/hw/rild u:object_r:rild_exec:s0
+/dev/socket/rildOemHook u:object_r:rild_socket:s0
+
6.添加4g上网图标
device\rockchip\rk356x\overlay\frameworks\base\core\res\res\value\config.xml
<string-array translatable="false" name="networkAttributes">
<item>"wifi,1,1,2,-1,true"</item>
+ <item>"mobile,0,0,0,-1,true"</item>
+ <item>"mobile_mms,2,0,2,60000,true"</item>
+ <item>"mobile_supl,3,0,2,60000,true"</item>
+ <item>"mobile_dun,4,0,2,60000,true"</item>
+ <item>"mobile_hipri,5,0,3,60000,true"</item>
+ <item>"mobile_fota,10,0,2,60000,true"</item>
+ <item>"mobile_ims,11,0,2,60000,true"</item>
+ <item>"mobile_cbs,12,0,2,60000,true"</item>
<item>"bluetooth,7,7,0,-1,true"</item>
<item>"ethernet,9,9,9,-1,true"</item>
7.添加4g物联卡的apn配置
在\vendor\rockchip\common\phone\etc\apns-full-conf.xml中增加
<apn carrier="China Telecom" mcc="460" mnc="11" apn="ctnet" type="default,supl" />
<apn carrier="中国移动物联网4G" mcc="460" mnc="04" apn="cmiot" type="default,supl" />
<apn carrier="中国移动物联网2G" mcc="460" mnc="04" apn="cmmtm" type="default,supl" />
<apn carrier="中国联通物联网gzm2mapn" mcc="460" mnc="06" apn="unim2m.gzm2mapn" port="80" type="default,supl" />
<apn carrier="中国联通物联网njm2mapn" mcc="460" mnc="06" apn="unim2m.njm2mapn" type="default,supl" />
<apn carrier="中国电信物联网m2m" mcc="460" mnc="03" apn="CTNET" user="m2m" password="vnet.mobi" type="default" />
8.修改对应的daemon
进入hardware/ril/rild/rild.rc
+#service vendor.ril-daemon /vendor/bin/hw/rild
+# class main
+# user radio
+# group radio cache inet misc audio log readproc wakelock
+# capabilities BLOCK_SUSPEND NET_ADMIN NET_RAW
+#for ec200m-cn
+service ril-daemon /vendor/bin/hw/rild -l /vendor/lib64/hw/libreference-ril.so
class main
+ user radio
+ group radio cache inet misc audio log readproc wakelock
+ user root
+ group radio cache inet misc audio sdcard_rw log
9.遇到开机未插4G模块,就一直报错的问题
打印报错:
[ 2650.466038] init: Control message: Could not find 'android.hardware.radio@1.1::IRadio/slot1' for ctl.interface_start from pid: 149 (/system/bin/hwservicemanager)
解决:
RK3568\system\core\init\init.cpp
static bool HandleControlMessage(std::string_view message, const std::string& name,
pid_t from_pid) {
std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid);
std::string process_cmdline;
......
const auto& function = it->second;
if (auto result = function(service); !result.ok()) {
LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name
<< "' from pid: " << from_pid << " (" << process_cmdline
<< "): " << result.error();
return false;
}
+ if(strcmp(name.c_str(),"android.hardware.radio@1.1::IRadio/slot1") != 0) {
LOG(INFO) << "Control message: Processed ctl." << message << " for '" << name
<< "' from pid: " << from_pid << " (" << process_cmdline << ")";
+ }
return true;
}
RK3568\system\hwservicemanager\ServiceManager.cpp
static void tryStartService(const std::string& fqName, const std::string& name) {
using ::android::base::SetProperty;
// The "happy path" here is starting up a service that is configured as a
// lazy HAL, but we aren't sure that is the case. If the service doesn't
// have an 'interface' entry in its .rc file OR if the service is already
// running, then this will be a no-op. So, for instance, if a service is
// deadlocked during startup, you will see this message repeatedly.
+ if(strcmp(fqName.c_str(),"android.hardware.radio@1.1::IRadio") != 0) {
LOG(INFO) << "Since " << fqName << "/" << name
<< " is not registered, trying to start it as a lazy HAL.";
+ }
std::thread([=] {
(void)SetProperty("ctl.interface_start", fqName + "/" + name);
}).detach();
}
RK3568\system\libhidl\transport\ServiceManagement.cpp
void wait(bool timeout) {
using std::literals::chrono_literals::operator""s;
if (!mRegisteredForNotifications) {
// As an alternative, just sleep for a second and return
LOG(WARNING) << "Waiting one second for " << mInterfaceName << "/" << mInstanceName;
sleep(1);
return;
}
std::unique_lock<std::mutex> lock(mMutex);
do {
mCondition.wait_for(lock, 1s, [this]{
return mRegistered;
});
if (mRegistered) {
break;
}
+ if(strcmp(mInterfaceName.c_str(),"android.hardware.radio@1.1::IRadio") != 0) {
LOG(WARNING) << "Waited one second for " << mInterfaceName << "/" << mInstanceName;
+ }
} while (!timeout);
}
sp<::android::hidl::base::V1_0::IBase> getRawServiceInternal(const std::string& descriptor,
const std::string& instance,
bool retry, bool getStub) {
// In case of legacy or we were not asked to retry, don't.
if (vintfLegacy || !retry) break;
if (waiter != nullptr) {
+ if(strcmp(descriptor.c_str(),"android.hardware.radio@1.1::IRadio") != 0) {
ALOGI("getService: Trying again for %s/%s...", descriptor.c_str(), instance.c_str());
+ }
waiter->wait(true /* timeout */);
}
}
return nullptr;
}
10 .添加rild后一直报以下log:
这是因为你的so库文件,肯定和该模块不匹配导致的,可以pull出来对比一下。
(本人踩过的坑,用的是供应商提供的ec200m-cn的库,结果就是不匹配,后面是找到了之前调通过的库文件,push进去才匹配的)
四、验证
1.输入命令ifconfig,会出现节点usb0/wwan0
2.插入你的sim卡,上述都无误的话,应该是直接出现以下例图
ping网址能通说明没问题了。
五、日志抓取
如有其他问题可进行日志抓取,抓取模块log方法如下:
adb root
adb remount
adb shell mkdir /data/quectel_debug_log
adb shell chmod 777 /data/quectel_debug_log
adb reboot