Linux USB驱动框架:
USB 是一种分层总线结构。USB 设备与主机之间的数据传输由 USB 控制器控制。Linux USB 驱动程序架构如下图所示。Linux USB 主机驱动包括三部分:USB 主机控制器驱动、USB 核心和 USB 设备驱动。
模块加载 USB 转串口 option 驱动程序后,在/dev 目录下创建 ttyUSB0、ttyUSB1 和 ttyUSB2 等设备文件。以下章节介绍如何将 USB 转串口 option 驱动程序移植到 Linux 操作系统中。
一、驱动移植
// 需要修改的内核配置
longan/kernel/linux-4.9/.config// 需要修改的驱动文件
longan/kernel/linux-4.9/drivers/usb/serial/option.c
longan/kernel/linux-4.9/drivers/usb/serial/usb_wwan.c// 需要用到的驱动文件
longan/kernel/linux-4.9/drivers/net/usb/cdc-acm.c
longan/kernel/linux-4.9/drivers/net/usb/cdc-ether.c
1.在 option_ids 列表内增加 EC200M-CN 的 PID\VID, 这样才能识别到该 USB 设备为串口设备,文档内可查。
// linux-4.9/drivers/usb/serial/option.c
static const struct usb_device_id option_ids[] = {
#ifdef SUPPORT_QUECTEL{ USB_DEVICE(0x2C7C, 0x6002) }, // support EC200S/EC200M
#endif......
}
2.一个 USB 设备可以有多个功能不同的接口,在 option_ids 添加该设备的 PID\VID 后,会导致该设备的所有接口都会绑定到 USB Serial Option 驱动上,导致 USBNet 驱动接口无法正常工作,因此需要在 option_probe 中根据类码、接口索引、端点数量、子类码将 USBNet 的接口排除出来。
// linux-4.9/drivers/usb/serial/option.c
static int option_probe(struct usb_serial *serial, const struct usb_device_id *id)
{......
#ifdef SUPPORT_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, ADBreturn -ENODEV;}if ((idProduct&0xF000) == 0x0000) {//MDM interface 4 is QMIif (intf->bInterfaceNumber == 4 && intf->bNumEndpoints == 3 && intf->bInterfaceSubClass == 0xFF && intf->bInterfaceProtocol == 0xFF)return -ENODEV;}}#ifdef SUPPORT_QUECTEL_AUTO_SUSPEND//For USB Auto Suspendif (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000);usb_enable_autosuspend(serial->dev);}#endif #ifdef SUPPORT_QUECTEL_REMOTE_WAKEUP//For USB Remote Wakeupif (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup}#endif
#endif/* Store the device flags so we can use them during attach. */usb_set_serial_data(serial, (void *)device_flags);return 0;
}
3.根据USB协议的要求,在批量输出传输期间,通过设置 URB_ZERO_PACKET 标志来添加处理零数据包的机制。
diff --git a/longan/kernel/linux-4.9/drivers/usb/serial/usb_wwan.c b/longan/kernel/linux-4.9/drivers/usb/serial/usb_wwan.c
old mode 100644
new mode 100755
index 3dfdfc8..e56b275
--- a/longan/kernel/linux-4.9/drivers/usb/serial/usb_wwan.c
+++ b/longan/kernel/linux-4.9/drivers/usb/serial/usb_wwan.c
@@ -36,6 +36,8 @@#include <linux/serial.h>#include "usb-wwan.h"+#define SUPPORT_QUECTEL 1
+/** Generate DTR/RTS signals on the port using the SET_CONTROL_LINE_STATE request* in CDC ACM.
@@ -504,6 +506,14 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,usb_fill_bulk_urb(urb, serial->dev,usb_sndbulkpipe(serial->dev, endpoint) | dir,buf, len, callback, ctx);
+
+#ifdef SUPPORT_QUECTEL
+ 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;
+ }
+#endifreturn urb;}
4.增加 USB 控制器复位后恢复操作
// linux-4.9/drivers/usb/serial/option.c
static struct usb_serial_driver option_1port_device = {......
#ifdef SUPPORT_QUECTEL.reset_resume = usb_wwan_resume,
#endif
};
5.在内核中启用 USB SERIAL 配置
CONFIG_USB_SERIAL=y
CONFIG_USB_SERIAL_WWAN=y
CONFIG_USB_SERIAL_OPTION=y
CONFIG_USB_ACM=y
CONFIG_USB_NET_DRIVERS=y
CONFIG_USB_USBNET=y
CONFIG_USB_NET_CDCETHER=y
6.配置内核
(1):执行以下命令切换到内核目录cd <用户内核目录>(2):执行以下命令编译内核。make menuconfig(3):启用配置项。选择<*>表示将驱动程序编译到内核映像。选择<M>表示将驱动程序编译成模块。
以 USB 转串口 option 驱动为例,用户可以通过以下选项启用CONFIG_USB_SERIAL_OPTION,将USB 转串口 option 驱动编译到内核镜像。
二、系统适配
主要是修改 ril 库相关的文件,实现拨号和衔接数据通路。
1.ril 库移植
涉及文件列表
android/hardware/ril/rild/radio.xml
android/hardware/ril/rild/rild.rc
android/device/softwinner/common/sepolicy/vendor/rild.te
android/vendor/aw/public/prebuild/lib/librild/radio_common.mk
android/vendor/aw/public/prebuild/lib/librild/lib/lib32/libquectel-ril.so
android/vendor/aw/public/prebuild/lib/librild/lib/lib64/libquectel-ril.so
构建文件拷贝脚本,将移远的 ril 库以及 apns-conf.xml、ql-ril.conf 文件更新到系统指定目录下
android/vendor/aw/public/prebuild/lib/librild/lib/lib32/libquectel-ril.so
android/vendor/aw/public/prebuild/lib/librild/lib/lib64/libquectel-ril.so
三、查看系统属性
mercury-demo:/ # getprop | grep ril
getprop | grep ril
[gsm.version.ril-impl]: [Quectel_Android_RIL_Driver_V3.5.0]
[init.svc.vendor.ril-daemon]: [running]
[ro.boottime.vendor.ril-daemon]: [9918673186]
[ro.radio.noril]: [false]
[vendor.rild.libargs]: [-d/dev/ttyUSB2]
[vendor.rild.libpath]: [/vendor/lib64/libquectel-ril.so]
以上信息包括ril库的版本,ril守护进程的运行状态,还有ril库文件的路径等等,在移植相应ril库文件后EC200M模块可正常上网。
四、日志抓取
如有其他问题可进行抓取日志分析,抓取模块Log方法如下:
adb root
adb shell mkdir /data/quectel_debug_log
adb shell chmod 777 /data/quectel_debug_log
adb reboot