OpenWrt下移植EC20R2.0驱动实现4G模块实现qmi拨号上网
本帖最后由 fannifu 于 2024-4-18 09:22 编辑OpenWrt下移植EC20R2.0驱动实现4G模块实现qmi拨号上网
1.参考资料
本文档参考资料为Quectel_WCDMA<E_Linux_USB_Driver_User_Guide_V1.6.pdf,实现QMI拨号,只需要参考下列章节即可https://www.openwrt.pro/ueditor/php/upload/image/20210609/1623218971918464.pngEC20有多个版本,看是否是EC20 R2.0还是普通EC20。
2.修改内核代码
2.1.0. 修改USB串口驱动,增加PID&VID,相关QUECTEL USB产品定义(枚举信息)
修改内核源码文件 File : /drivers/usb/serial/option.c
staticconst struct usb_device_id option_ids[] = {
#if 1 //Added by Quectel
{ USB_DEVICE(0x05C6, 0x9090) }, /* Quectel UC15 */
{ USB_DEVICE(0x05C6, 0x9003) }, /* Quectel UC20 */
{ USB_DEVICE(0x05C6, 0x9215) }, /* Quectel EC20 */
{ USB_DEVICE(0x2C7C, 0x0125) }, /* Quectel EC25/EC20 R2.0 */
{ USB_DEVICE(0x2C7C, 0x0121) }, /* Quectel EC21 */
#endif
2.1.1.添加零包处理 Add the Zero PacketMechanism
For Linux Kernel Version newer than 2.6.34:Linux 2.6.34以前的版本修改内核源码文件 File:/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 Quectelfor Zero Packet
if (dir == USB_DIR_OUT) {
struct usb_device_descriptor*desc = &serial->dev->descriptor;
if (desc->idVendor ==cpu_to_le16(0x05C6) && desc->idProduct == cpu_to_le16(0x9090))
urb->transfer_flags|= URB_ZERO_PACKET;
if (desc->idVendor ==cpu_to_le16(0x05C6) && desc->idProduct == cpu_to_le16(0x9003))
urb->transfer_flags|= URB_ZERO_PACKET;
if (desc->idVendor ==cpu_to_le16(0x05C6) && desc->idProduct == cpu_to_le16(0x9215))
urb->transfer_flags|= URB_ZERO_PACKET;
if (desc->idVendor ==cpu_to_le16(0x2C7C))
urb->transfer_flags|= URB_ZERO_PACKET;
}
#endif
return urb;
}
2.1.2. 增加休眠后唤醒接口 Add Reset Resume
修改内核源码文件 File: /drivers/usb/serial/option.cstatic struct usb_serial_driveroption_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
}
2.2.5使用QMI WWAN Use GobiNet or QMI WWANFile: /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
//Quectel UC20'sinterface 4 can be used as USB Network device
if(serial->dev->descriptor.idVendor== cpu_to_le16(0x05C6)&&
serial->dev->descriptor.idProduct ==cpu_to_le16(0x9003)
&&serial->interface->cur_altsetting->desc.bInterfaceNumber >= 4)
return -ENODEV;
//Quectel EC20's interface4 can be used as USB Network device
if(serial->dev->descriptor.idVendor== cpu_to_le16(0x05C6)&&
serial->dev->descriptor.idProduct== cpu_to_le16(0x9215)
&&serial->interface->cur_altsetting->desc.bInterfaceNumber >= 4)
return -ENODEV;
//QuectelEC21&EC25&EC20 R2.0's interface 4 can be used as USB Network device
if(serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)
&&serial->interface->cur_altsetting->desc.bInterfaceNumber >= 4)
return -ENODEV;
#endif
/* Store device id so we can use it duringattach. */
usb_set_serial_data(serial, (void *)id);
return 0;
}
3. 修改内核增加QMI WWAN驱动支持QMI WWAN Driver
3.1.1 增加VID和PID Add VID and PID
修改文件 "/drivers/net/usb/qmi_wwan.c".
static const struct usb_device_idproducts[] = {
#if 1 //Added by Quectel
#ifndef QMI_FIXED_INTF
/* map QMI/wwan function by a fixed interface number */
#define QMI_FIXED_INTF(vend, prod, num) \
.match_flags=USB_DEVICE_ID_MATCH_DEVICE|
USB_DEVICE_ID_MATCH_INT_INFO, \
.idVendor = vend, \
.idProduct = prod, \
.bInterfaceClass = 0xff, \
.bInterfaceSubClass = 0xff, \
.bInterfaceProtocol = 0xff, \
.driver_info = (unsigned long)&qmi_wwan_force_int##num,
#endif
{ QMI_FIXED_INTF(0x05C6, 0x9003, 4) }, /* Quectel UC20 */
{ QMI_FIXED_INTF(0x05C6, 0x9215, 4) }, /* Quectel EC20 */
{ QMI_FIXED_INTF(0x2C7C, 0x0125, 4) }, /* Quectel EC25/EC20R2.0 */
{ QMI_FIXED_INTF(0x2C7C, 0x0121, 4) }, /* Quectel EC21 */
#endif
3.1.2增加对Raw IP Mode的支持 Add Support for Raw IP Modefor EC21/EC25/EC20 R2.0
修改文件"/drivers/net/usb/qmi_wwan.c"#include <linux/usb/usbnet.h>
#include <linux/usb/cdc-wdm.h>
#if 1 //Added by Quectel
#include <linux/etherdevice.h>
struct sk_buff *qmi_wwan_tx_fixup(struct usbnet *dev, structsk_buff *skb, gfp_t flags)
{
if (dev->udev->descriptor.idVendor !=cpu_to_le16(0x2C7C))
return skb;
// Skip Ethernet header from message
if (skb_pull(skb, ETH_HLEN)) {
return skb;
} else {
dev_err(&dev->intf->dev, "Packet Dropped");
}
// Filter the packet out, release it
dev_kfree_skb_any(skb);
return NULL;
}
#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 ))
static int qmi_wwan_rx_fixup(struct usbnet *dev, structsk_buff *skb)
{
__be16 proto;
if (dev->udev->descriptor.idVendor !=cpu_to_le16(0x2C7C))
return 1;
/* This check is no longer done by usbnet */
if (skb->len < dev->net->hard_header_len)
return 0;
switch (skb->data & 0xf0) {
case 0x40:
proto = htons(ETH_P_IP);
break;
case 0x60:
proto = htons(ETH_P_IPV6);
break;
case 0x00:
if (is_multicast_ether_addr(skb->data))
return 1;
/* possibly bogus destination - rewrite just in case */
skb_reset_mac_header(skb);
goto fix_dest;
default:
/* pass along other packets without modifications */
return 1;
}
if (skb_headroom(skb) < ETH_HLEN)
return 0;
skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
eth_hdr(skb)->h_proto = proto;
memset(eth_hdr(skb)->h_source, 0, ETH_ALEN);
fix_dest:
memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr,ETH_ALEN);
return 1;
}
/* very simplistic detection of IPv4or IPv6 headers */
static bool possibly_iphdr(const char*data)
{
return (data & 0xd0) == 0x40;
}
#endif
#endif
/* if follow function exist, modify itas below */
static int qmi_wwan_bind(struct usbnet*dev, struct usb_interface *intf)
{
……
#if 1 //Added by Quectel
if(dev->udev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
dev_info(&intf->dev, "Quectel EC21&EC25&EC20 R2.0work on RawIP mode\n");
dev->net->flags |= IFF_NOARP;
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 ))
/* make MAC addr easily distinguishable from an IP header */
if (possibly_iphdr(dev->net->dev_addr)){
dev->net->dev_addr |= 0x02; /* set local assignment bit */
dev->net->dev_addr &= 0xbf; /* clear "IP" bit */
}
#endif
usb_control_msg(
interface_to_usbdev(intf),
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
0x22,//USB_CDC_REQ_SET_CONTROL_LINE_STATE
0x21,//USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
1, //active CDCDTR
intf->cur_altsetting->desc.bInterfaceNumber,
NULL, 0, 100);
}
#endif
err:
return status;
}
/* if follow struct exist, modify itas below */
static const struct driver_infoqmi_wwan_info =
{
……
#if 1 //Added by Quectel
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 4,5,0 ))
.tx_fixup =qmi_wwan_tx_fixup,
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 ))
.rx_fixup =qmi_wwan_rx_fixup,
#endif
#endif
}
/* if follow struct exist, modify itas below */
static const struct driver_infoqmi_wwan_force_int4 = {
……
#if 1 //Added by Quectel
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 4,5,0 ))
.tx_fixup =qmi_wwan_tx_fixup,
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 ))
.rx_fixup =qmi_wwan_rx_fixup,
#endif
#endif
};
4. 编译配置
使用图形化菜单配置openwrt, 输入命令:
make menuconfig
配置项:
NetWork >>
<*> uqmi......................... Control utility for mobile broadband modems
-*- wwan.....................GenericOpenWrt 3G/4G proto handler
Kernel modules >>
USB Support >>
-*- Kmod -usb-core
-*- Kmod -usb-net
-*- Kmod-usb-net-cdc-ether
-*- Kmod-usb-net-qmi-wwan
< >Kmod-usb-ohci //这个选项一定要勾选,否则可能无法在系统中查看设备
<*> Kmod-usb-serial
<*> Kmod-usb-serial-option
<> Kmod-usb-uhci
<*> Kmod-usb2
5. 编译
make命令编译:
make -j10
编译方法
参考:
【openwrt】OpenWrt编译 – 说明-谷动谷力 (sunsili.com)
【Openwrt】开发环境搭建 编译openwrt源码-谷动谷力 (sunsili.com)
【openwrt学习笔记一】从搭建编译环境到编译openwrt全过程-谷动谷力 (sunsili.com)
6. 调试
6.0. 固件下载到开发板
编译成功过后,固件下载到开发板,使用命令:scp bin/ramips/openwrt-ramips-mt7628-mt7628-squashfs-sysupgrade.bin root@192.168.3.157:/tmp
root@192.168.3.157's password:
Permission denied, please try again.
root@192.168.3.157's password:
openwrt-ramips-mt7628-mt7628-squashfs-sysupgrade.bin 100% 8448KB 938.7KB/s 00:09
6. 1. 升级固件
在开发板上执行以下命令:sysupgrade /tmp/openwrt-ramips-mt7628-mt7628-squashfs-sysupgrade.bin
参考:openwrt怎么升级固件?使用命令sysupgrade实现openwrt升级固件-谷动谷力 (sunsili.com)
查看EC20驱动是否成功升级固件成功后, 开发板会自动重启,可以看到Openwrt系统启动打印信息:[ 0.220000] drivers/phy/phy-ralink-usb.c:ralink_usb_phy_probe
[ 0.230000] drivers/phy/phy-ralink-usb.c:ralink_usb_phy_probe
[ 5.930000] usbcore: registered new interface driver usbfs
[ 5.930000] usbcore: registered new interface driver hub
[ 5.940000] usbcore: registered new device driver usb
[ 6.200000] usb usb1: no of_node; not parsing pinctrl DT
[ 6.310000] usb usb2: no of_node; not parsing pinctrl DT
[ 6.530000] usb 1-1: new high-speed USB device number 2 using ehci-platform
[ 6.690000] usb 1-1: no of_node; not parsing pinctrl DT
[ 44.830000] usbcore: registered new interface driver cdc_wdm
[ 44.890000] usbcore: registered new interface driver usbserial
[ 44.900000] usbcore: registered new interface driver usbserial_generic
[ 44.900000] usbserial: USB Serial support registered for generic
[ 44.960000] usbcore: registered new interface driver cdc_ether
[ 44.970000] usbcore: registered new interface driver cdc_ncm
[ 44.980000] usbcore: registered new interface driver cdc_subset
[ 45.040000] qmi_wwan 1-1:1.4 wwan0: register 'qmi_wwan' at usb-101c0000.ehci-1, WWAN/QMI device, 2a:d6:d2:f8:d3:30
[ 45.050000] usbcore: registered new interface driver qmi_wwan
[ 45.060000] usbcore: registered new interface driver rndis_host
[ 45.070000] usbcore: registered new interface driver cdc_mbim
[ 45.080000] usbcore: registered new interface driver option
[ 45.090000] usbserial: USB Serial support registered for GSM modem (1-port)
启动后,可以使用命令查看
dmesg | grep usb
[ 0.220000] drivers/phy/phy-ralink-usb.c:ralink_usb_phy_probe
[ 0.230000] drivers/phy/phy-ralink-usb.c:ralink_usb_phy_probe
[ 5.930000] usbcore: registered new interface driver usbfs
[ 5.930000] usbcore: registered new interface driver hub
[ 5.940000] usbcore: registered new device driver usb
[ 6.200000] usb usb1: no of_node; not parsing pinctrl DT
[ 6.310000] usb usb2: no of_node; not parsing pinctrl DT
[ 6.530000] usb 1-1: new high-speed USB device number 2 using ehci-platform
[ 6.690000] usb 1-1: no of_node; not parsing pinctrl DT
[ 44.830000] usbcore: registered new interface driver cdc_wdm
[ 44.890000] usbcore: registered new interface driver usbserial
[ 44.900000] usbcore: registered new interface driver usbserial_generic
[ 44.900000] usbserial: USB Serial support registered for generic
[ 44.960000] usbcore: registered new interface driver cdc_ether
[ 44.970000] usbcore: registered new interface driver cdc_ncm
[ 44.980000] usbcore: registered new interface driver cdc_subset
[ 45.040000] qmi_wwan 1-1:1.4 wwan0: register 'qmi_wwan' at usb-101c0000.ehci-1, WWAN/QMI device, 2a:d6:d2:f8:d3:30
[ 45.050000] usbcore: registered new interface driver qmi_wwan
[ 45.060000] usbcore: registered new interface driver rndis_host
[ 45.070000] usbcore: registered new interface driver cdc_mbim
[ 45.080000] usbcore: registered new interface driver option
[ 45.090000] usbserial: USB Serial support registered for GSM modem (1-port)
查看usb枚举信息, 可用命令lsusblsusb
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 002: ID 2c7c:0125
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
查看dev下设备号 ls /devls /dev/tty*
/dev/tty /dev/ttyS0 /dev/ttyS1 /dev/ttyS2 /dev/ttyUSB0/dev/ttyUSB1/dev/ttyUSB2/dev/ttyUSB3
出现以上信息,表示驱动正常,下面可以启动拨号软件进行拨号。其中 /dev/ttyUSB0/dev/ttyUSB1/dev/ttyUSB2/dev/ttyUSB3为EC20枚举4个串口
6.2. 测试4G上网
参考:OpenWRT EC20 4G网卡自动拔号配置方法 -谷动谷力 (sunsili.com)
6.3. 测试打电话
参考:【linux】Openwert 环境下移远EC20 拔打电话播放文本语音方法...-谷动谷力 (sunsili.com)
EC20有4种上网模式,可以通过如下指令切换模式。cat /dev/ttyUSB2 &echo -e "AT+QCFG=\"usbnet\",0\r\n" > /dev/ttyUSB2 #设定模式
0-3echo -e "AT+CFUN=1,1\r\n" >/dev/ttyUSB2 #重启模块四种模式分别为:
[*]0RMNET接口,通过QMI工具发的QMI命令,获取公网IP。
[*]1ECM接口,通过标准的CDC-ECM发起data call,是发送标准的ECM命令,获取局域网ip。
[*]2MBIM接口,Mobile Broadband Interface Model,正宗的移动宽带接口模型,专门用于3G/4G/5G模块的,只在win8以上的windows上使用。
[*]3RNDIS接口,基于USB实现RNDIS实际上就是TCP/IP over USB,就是在USB设备上跑TCP/IP,让USB设备看上去像一块网卡获取局域网ip。
在openwrt下能用的主要是013模式,其中Rmnet模式的qmi拨号设置非常繁琐,需要在源码注入多处驱动,还要自编译拨号程序,所以就没有试,网上有相关教程。设置比较简单是ECM、RNDIS模式,这两种模式在设置上没什么区别,都是获取的局域网IP,就是安装的插件不一样。上面建议的插件已经都包含了两种模式所需。
在luci->网络->接口,设置wan接口,在物理设置选项卡里面设置,发现多了一项硬件接口(usb0,或者wwan),选中它,保存并应用设置,过一会儿就会发现路由器wan口获取到了192.168..的局域网IP地址。通过设置wan6口有可能可以获取ipv6,在ec20模块需要设置:echo -e "AT+CGDCONT=1,\"IPV4V6\",\"3gnet\"\r\n" > /dev/ttyUSB2 #设置上下文
echo -e "AT+CGACT=1,1\r\n" > /dev/ttyUSB2 #激活上下文
echo -e "AT+CFUN=1,1\r\n" >/dev/ttyUSB2
页:
[1]