1 DTS
本dts的内容不是必须的,也可以自己手动拉gpio去操控。
rk_modem: rk-modem {
compatible="4g-modem-platdata";
4G,vbat-gpio = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>;
//4G,power-gpio = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>;
4G,reset-gpio = <&gpio2 RK_PD7 GPIO_ACTIVE_HIGH>;
status = "okay";
};
对应的驱动为:kernel/drivers/net/lte/lte_rm310.c
2 内核驱动
新一点的内核(包含3568的4.19)都是已经包含了移远的option驱动的,无需再添加vid和pid。但是零包机制、电源管理以及第四个用于usbnet的口相关的代码还是有必要加上去的。即:
kernel/drivers/usb/serial/option.c
static struct usb_serial_driver option_1port_device = {
.driver = {
.owner = THIS_MODULE,
.name = "option1",
},
.description = "GSM modem (1-port)",
.id_table = option_ids,
.num_ports = 1,
.probe = option_probe,
.open = usb_wwan_open,
.close = usb_wwan_close,
.dtr_rts = usb_wwan_dtr_rts,
.write = usb_wwan_write,
.write_room = usb_wwan_write_room,
.chars_in_buffer = usb_wwan_chars_in_buffer,
.tiocmget = usb_wwan_tiocmget,
.tiocmset = usb_wwan_tiocmset,
.ioctl = usb_wwan_ioctl,
.attach = option_attach,
.release = option_release,
.port_probe = usb_wwan_port_probe,
.port_remove = usb_wwan_port_remove,
.read_int_callback = option_instat_callback,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
.resume = usb_wwan_resume,
+#if 1 //Added by Quectel
+ .reset_resume = usb_wwan_resume,
+#endif
#endif
};
static int option_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
struct usb_interface_descriptor *iface_desc =
&serial->interface->cur_altsetting->desc;
unsigned long device_flags = id->driver_info;
/* Never bind to the CD-Rom emulation interface */
if (iface_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE)
return -ENODEV;
/*
* Don't bind reserved interfaces (like network ones) which often have
* the same class/subclass/protocol as the serial interfaces. Look at
* the Windows driver .INF files for reserved interface numbers.
*/
if (iface_is_reserved(device_flags, iface_desc->bInterfaceNumber))
return -ENODEV;
/*
* Allow matching on bNumEndpoints for devices whose interface numbers
* can change (e.g. Quectel EP06).
*/
if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2)
return -ENODEV;
+#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 the device flags so we can use them during attach. */
usb_set_serial_data(serial, (void *)device_flags);
return 0;
}
rk3568/kernel/drivers/usb/serial/usb_wwan.c
static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
int endpoint,
int dir, void *ctx, char *buf, int len,
void (*callback) (struct urb *))
{
struct usb_serial *serial = port->serial;
struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
struct urb *urb;
struct usb_device_descriptor *desc = &serial->dev->descriptor;
urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
if (!urb)
return NULL;
usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev, endpoint) | dir,
buf, len, callback, ctx);
if (intfdata->use_zlp && dir == USB_DIR_OUT)
urb->transfer_flags |= URB_ZERO_PACKET;
if (dir == USB_DIR_OUT) {
if ((desc->idVendor == cpu_to_le16(0x1286) &&
desc->idProduct == cpu_to_le16(0x4e3c)))
urb->transfer_flags |= URB_ZERO_PACKET;
}
+#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;
}
添加好虚拟出的各个串口后,要加入usbnet的驱动,可以使用GobiNet或者wwan。Gobinet是以前Linux内核没有添加相关驱动时的产物。目前使用wwan比较多。找到移远提供的qmai_wwan_q.c放入kernel/drivers/net/usb文件夹下,并修改Makefile:
+obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan_q.o #最好放在qmi_wwan.o之前
obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o
此驱动会生成wwan0(用ifconfig -a可以看到),以及/dev/cdc-wdm0设备。在Android中,需要修改部分代码才可以添加此设备:
rk3568/system/core/init/devices.cpp
void DeviceHandler::HandleUevent(const Uevent& uevent) {
...
if (uevent.subsystem == "block") {
block = true;
devpath = "/dev/block/" + Basename(uevent.path);
if (StartsWith(uevent.path, "/devices")) {
links = GetBlockDeviceSymlinks(uevent);
}
} else if (const auto subsystem =
std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
subsystem != subsystems_.cend()) {
devpath = subsystem->ParseDevPath(uevent);
} else if (uevent.subsystem == "usb") {
if (!uevent.device_name.empty()) {
devpath = "/dev/" + uevent.device_name;
} else {
// This imitates the file system that would be created
// if we were using devfs instead.
// Minors are broken up into groups of 128, starting at "001"
int bus_id = uevent.minor / 128 + 1;
int device_id = uevent.minor % 128 + 1;
devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
}
+#if 1 // add by quectel for mknod /dev/cdc-wdm0
+ } else if(uevent.subsystem == "usbmisc" && !uevent.device_name.empty()){
+ devpath = "/dev/" + uevent.device_name;
+#endif
} else if (StartsWith(uevent.subsystem, "usb")) {
// ignore other USB events
return;
} else {
devpath = "/dev/" + Basename(uevent.path);
}
...
}
至于ECM等驱动也都是usbnet使用的,可以选择不添加。
3 Android
3.1 复制文件
首先要找到FAE要到适合自己android版本的对应的资料,其中包括文档、ril库等。
将libreference-ril.so拷贝并改名为:vendor/rockchip/common/phone/lib/libreference-ril-quel.so
将ql-ril.conf拷贝至:vendor/rockchip/common/phone/lib/ql-ril.conf
3.2 设置资源文件将复制的目标路径
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/lib/libreference-ril-quel.so:vendor/lib64/libreference-ril-quel.so \
$(CUR_PATH)/phone/lib/ql-ril.conf:system/etc/ql-ril.conf
3.3 SELINUX和rild服务
selinux部分需要注意的是通常文件最后需要有一个空行作为结尾。
device/rockchip/common/ueventd.rockchip.rc
+/dev/ttyACM* 0660 radio radio
+/dev/cdc-wdm* 0660 radio radio
+/dev/qcqmi* 0660 radio radio
+/dev/cdc-acm* 0660 radio radio
+/dev/mhi_DUN 0660 radio radio
+/dev/mhi_DIAG 0660 radio radio
+/dev/mhi_BHI 0660 radio radio
+/dev/mhi_LOOPBACK 0660 radio radio
+/dev/mhi_QMI0 0660 radio radio
+
device/rockchip/common/sepolicy/private/file_contexts
+/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
+
+#pcie
+/dev/mhi_DUN u:object_r:radio_device:s0
+/dev/mhi_DIAG u:object_r:radio_device:s0
+/dev/mhi_BHI u:object_r:radio_device:s0
+/dev/mhi_LOOPBACK u:object_r:radio_device:s0
+/dev/mhi_QMI0 u:object_r:radio_device:s0
+
system/sepolicy/vendor/rild.te
+allow rild self:packet_socket { create bind write read };
+
对应rild可以修改为:
rk3568/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
+service ril-daemon /vendor/bin/hw/rild -l /vendor/lib64/libreference-ril-quel.so
+ class main
+ user radio
+ group radio cache inet misc audio sdcard_rw log
+ capabilities BLOCK_SUSPEND NET_ADMIN NET_RAW
3.4 修改BoardConfig.mk
SDK默认4G和dongle是关闭的,需要开启:
device/rockchip/common/BoardConfig.mk
-BOARD_HAS_RK_4G_MODEM ?= false
+BOARD_HAS_RK_4G_MODEM ?= true
-BOARD_HAVE_DONGLE ?= false
+BOARD_HAVE_DONGLE ?= true
external/usb_modeswitch/usb_dongle/Android.mk
-#common_local_tidy_flags := -warnings-as-errors=clang-analyzer-security*,cert-*
3.5 修改device.mk
rk3568/device/rockchip/rk356x/device.mk
PRODUCT_PROPERTY_OVERRIDES += \
ro.ril.ecclist=112,911 \
ro.opengles.version=196610 \
wifi.interface=wlan0 \
+ rild.libpath=/vendor/lib64/libreference-ril-quel.so \
+ rild.libargs=-d /dev/ttyUSB2 \
rk3568/device/rockchip/common/device.mk
ifeq ($(strip $(BOARD_HAS_RK_4G_MODEM)),true)
PRODUCT_PACKAGES += \
CarrierDefaultApp \
CarrierConfig \
rild \
- librk-ril\
+ libreference-ril-quel \
dhcpcd
PRODUCT_COPY_FILES += vendor/rockchip/common/phone/etc/apns-full-conf.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/apns-conf.xml
PRODUCT_PACKAGES += \
android.hardware.radio@1.2-radio-service \
android.hardware.radio.config@1.0-service
PRODUCT_PROPERTY_OVERRIDES += \
ro.boot.noril=false \
ro.telephony.default_network=9
ifeq ($(strip $(TARGET_ARCH)), arm64)
PRODUCT_PROPERTY_OVERRIDES += \
- vendor.rild.libpath=/vendor/lib64/librk-ril.so
+ vendor.rild.libpath=/vendor/lib64/libreference-ril-quel.so
3.6 修改manifest.xml
device/rockchip/common/4g_modem/manifest.xml
<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.config</name>
+ <name>android.hardware.radio</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
- <name>IRadioConfig</name>
+ <name>IRadio</name>
- <instance>default</instance>
+ <instance>slot1</instance>
+ </interface>
</hal>
</manifest>
3.7 启动脚本去掉ril-daemon
device/rockchip/common/init.rk30board.rc
# for telephony function
-#on property:ro.boot.noril=true
-# setprop ro.radio.noril true
-# stop ril-daemon
3.8使能电话进程
3.8.1 设置→网络和互联网里没有“移动网络”选项
rk3568/device/rockchip/rk356x/overlay/frameworks/base/core/res/res/values/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,false"</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>
</string-array>
3.8.2 移动网络里没有APN选项
rk3568/device/rockchip/common/overlay/frameworks/base/core/res/res/values/config.xml
-<bool name="config_voice_capable">false</bool>
+<bool name="config_voice_capable">true</bool>
3.8.3 移动信号图无信号
rk3568/vendor/rockchip/common/phone/lib/ql-ril.conf
# 打开如下选项
LTE_Is_Report_SignalStrength=1
4 调试方式
4.1 内核
ls /dev/ | grep ttyUSB // 可以看到ttyUSB0-3说明成功注册上option驱动,其中/dev/ttyUSB2为AT指令口,可以收发AT指令。
ifconfig -a //如果可以看到wwan0则成功注册了 qmi_wwan驱动
# 如果正在运行rild可能导致抢AT指令资源,可以通过stop ril-daemon来先停止此服务
# AT指令简单的测试
> microcom /dev/ttyUSB2
ate1
AT
OK //看到OK说明AT指令收发成功
4.2 logcat
logcat -b radio // ril log
logcat -c -b radio // 清除之前的log
5 参考文档
移远官方:
Quectel_UMTS_LTE_5G_Linux_USB_Driver_User_Guide_V3.0
Android RILDriver User Guide
RK官方:
Rockchip_RM310_4G模块配置说明
Rockchip_Introduction_3G_Dongle_Configuration_CN&EN
他人博客: