1、开发环境
开发板:AR9331 Newifi3等
源码版本:Openwrt(源码地址:https://git.lede-project.org/source.git)
源码编译宿主系统:ubuntu12.04 ubuntu14.04版本或者以上都可以
4G模块:移远EC20 EC20 EC25 AG35等
参考文档:Quectel_WCDMA<E_Linux_USB_Driver_User_Guide_V1.7.pdf
2、操作步骤
【说明】虽然大家开发板型号不一,有AR、RT、MT版本;源码也有CC、LEDE、潘多拉、老毛子的不同;但是挂载4G驱动的方法都一样。
目前EC20和EC25对路由器最兼容,现在我是Openwrt最新版本的,兼容性完美,QMI拨号非常稳定。
2.1 USB Serial驱动
当4G模块连接到USB串行驱动时,驱动程序将在目录/dev中创建设备文件,
ttyUSB0/ttyUSB1/ttyUSB2/ttyUSB3/ttyUSB4
下图为各个4G模块 /ttyUSB的功能
我们的目的是需要cdc-wdm0 这个设备文件
接下来就是讲解如何移植USB Serial。
2.1.1 增加PID&VID
要想识别模块,客户应该在下面添加模块维和PID信息(支持EC20、EC20、EC25、AG35等4G模块)
File: [KERNEL]/drivers/usb/serial/option.c
比如AR9331的KERNEL目录在build_dir/target-mips_24kc_musl/linux-ar71xx_generic/linux-4.4.791
2
3
4
5
6
7
8
9
10
11
12
13static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(0x05C6, 0x9090) }, /* Quectel UC15 */
{ USB_DEVICE(0x05C6, 0x9003) }, /* Quectel UC20 */
{ USB_DEVICE(0x2C7C, 0x0125) }, /* Quectel EC25 */
{ USB_DEVICE(0x2C7C, 0x0121) }, /* Quectel EC21 */
{ USB_DEVICE(0x05C6, 0x9215) }, /* Quectel EC20 */
{ USB_DEVICE(0x2C7C, 0x0191) }, /* Quectel EG91 */
{ USB_DEVICE(0x2C7C, 0x0195) }, /* Quectel EG95 */
{ USB_DEVICE(0x2C7C, 0x0306) }, /* Quectel EG06/EP06/EM06 */
{ USB_DEVICE(0x2C7C, 0x0296) }, /* Quectel BG96 */
{ USB_DEVICE(0x2C7C, 0x0435) }, /* Quectel AG35 */
【注】只添加#if 1 到 #endif的内容,具体位置添加位置需要读者自己注意。
2.1.2 添加零包处理
根据USB协议的要求,客户需要添加处理零数据包的机制。
For Linux Kernel Version newer than 2.6.34:
File: [KERNEL]/drivers/usb/serial/usb_wwan.c
1 | static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint, |
2.1.3 增加休眠后唤醒接口
当MCU进入暂停/休眠模式时,一些USB主机控制器/USB集线器将失去电源或重新设置,并且在MCU退出暂停/休眠模式后,它们不能恢复USB设备。请添加以下语句以启用重新设置恢复过程。
For Linux kernel version higher than 3.4:
File: [KERNEL]/drivers/usb/serial/option.c
1 | static struct usb_serial_driver option_1port_device = { |
2.1.4 使用 GobiNet or QMI WWAN
如果客户使用ucxx/ec2x/egxx/EP06/EM06/BG96/AG35,并要求GobiNet或QMI WWAN,请添加以下语句,以防止这些模块接口4被用作USB串行设备。
For Linux Kernel Version newer than 2.6.30:
File: [KERNEL]/drivers/usb/serial/option.c
1 | static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { |
2.2 QMI WWAN驱动
2.2.1 Add VID and PID
QMI WWAN driver source file is [KERNEL]/drivers/net/usb/qmi_wwan.c.
File: [KERNEL]/drivers/net/usb/qmi_wwan.c
1 | static const struct usb_device_id products[] = { |
2.2.2 Add Support for Raw IP Mode
File: [KERNEL]/drivers/net/usb/qmi_wwan.c1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
struct sk_buff *qmi_wwan_tx_fixup(struct usbnet *dev, struct sk_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;
}
static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_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[0] & 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 IPv4 or IPv6 headers */
static bool possibly_iphdr(const char *data)
{
return (data[0] & 0xd0) == 0x40;
}
……
/* if follow function exist, modify it as below */
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
{
……
if (dev->udev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
dev_info(&intf->dev, "Quectel
EC25&EC21&EG91&EG95&EG06&EP06&EM06&BG96&AG35 work on RawIP mode\n");
dev->net->flags |= IFF_NOARP;
/* make MAC addr easily distinguishable from an IP header */
if (possibly_iphdr(dev->net->dev_addr)) {
dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */
dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */
}
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 CDC DTR
intf->cur_altsetting->desc.bInterfaceNumber,
NULL, 0, 100);
}
err:
return status;
}
……
/* if follow function exist, modify it as below */
static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf)
{
……
if (dev->udev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
dev_info(&intf->dev, "Quectel EC25&EC21&
EG91&EG95&EG06&EP06&EM06&BG96&AG35 work on RawIP mode\n");
dev->net->flags |= IFF_NOARP;
/* make MAC addr easily distinguishable from an IP header */
if (possibly_iphdr(dev->net->dev_addr)) {
dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */
dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */
}
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 CDC DTR
intf->cur_altsetting->desc.bInterfaceNumber,
NULL, 0, 100);
}
err:
return status;
}
……
/* if follow struct exist, modify it as below */
static const struct driver_info qmi_wwan_info =
{
……
.tx_fixup = qmi_wwan_tx_fixup,
.rx_fixup = qmi_wwan_rx_fixup,
}
……
/* if follow struct exist, modify it as below */
static const struct driver_info qmi_wwan_force_int4 = {
……
.tx_fixup = qmi_wwan_tx_fixup,
.rx_fixup = qmi_wwan_rx_fixup,
};
/* if follow struct exist, modify it as below */
static const struct driver_info qmi_wwan_shared = {
……
.tx_fixup = qmi_wwan_tx_fixup,
.rx_fixup = qmi_wwan_rx_fixup,
};
2.3 修改配置
第一步:进入配置环境1
make menuconfig
第二步:配置
1 | Kernel modules >> |
1 | NetWork >> |
1 | Utilities |
1 | Luci |
2.4 编译测试
以上操作完成后就是编译源码了。
1 | make V=s |
将编译好固件烧写进入板子中,插入4G模块,在dev目录下查看,就会出现cdc-wdm0 这个设备文件
有以上信息表示驱动配置成功,接下来就拨号了。
2.5 拨号上网
2.5.1 安装交叉编译工具
进入源码目录,执行mkae menuconfig1
2cd lede_source
make menuconfig
选中Toolchain
保存,编译1
make V=99
找到交叉工具链的位置:
这里我们演示将交叉工具链安装到 ubuntu 的“/opt”目录下。 首先切换到 openwrt 源码的根目录下,输入如下命令:
1 | cd ~/lede_source/bin/targets/ramips/mt7621 |
最后,设置环境变量1
sudo vi /etc/bash.bashrc
在最后一行添加1
2export PATH=/opt/openwrt-toolchain-ramips-mt7621_gcc-7.4.0_musl.Linux-x86_64/toolchain-mipsel_24kc_gcc-7.4.0_musl/bin:$PATH
export STAGING_DIR=/your_openwrt_path/staging_dir
《注意》上面这个“STAGING_DIR”变量中的“your_openwrt_path”是读者实际放 openwrt源码的根目录,如果“STAGING_DIR”变量不设置的话,会在用交叉工具链编译文件时有警告,但是不影响编译结果。最后保存退出。接着在终端执行以下命令:1
source /etc/bash.bashrc
测试是否安装成功:1
mipsel-openwrt-linux-gcc -v
2.5.2 编译拨号应用程序
拨号程序仍然使用移远提供的quectel-CM(下载地址在文章末尾),这是一个4G连接管理程序,这里没什么说的,执行:1
$mipsel-openwrt-linux-musl-gcc *.c -o quectel-CM -lpthread –ldl
【注】也可将quectel-CM下的Makefile文件修改成如下,然后 make1
2
3
4
5
6
7
8
9CROSS-COMPILE:= mips-openwrt-linux- #AR9331开发板的交叉编译工具(其他的自行修改)
CC:=$(CROSS-COMPILE)gcc
LD:=$(CROSS-COMPILE)ld
release: clean
$(CC) -Wall -s QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c udhcpc.c -o quectel-CM -lpthread -ldl
debug: clean
$(CC) -Wall -g QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c udhcpc.c -o quectel-CM -lpthread -ldl
clean:
rm -rf quectel-CM *~
【注】移远提供的拨号程序下载地址在文章末尾(包含参考文档)。
交叉编译后得到可执行文件:“quectel-CM” ,然后将quectel-CM可执行文件用wincp软件拷贝到AR9331开发板中,执行:1
2chmod 777 quectel-CM #先改变权限
./quectel-CM &
出现如下效果,说明拨号成功:
接下来ping一下百度首页地址,看是否可以联网。
说明测试成功,开发板可以上网了。
2.6 后续问题
问题1:能ping通百度IP地址,无法ping通其域名www.baidu.com
这里需要添加DNS解析服务器的地址,在/etc目录配置resolv.conf文件添加DNS客户,它包含了主机的域名搜索顺序和DNS服务器的地址。开发板执行:1
vim /etc/resolv.conf
编辑内容:1
2nameserver 114.114.114.114 #国内的DNS
nameserver 8.8.8.8 #国外的DNS
接下来,ping www.baidu.com
问题2:路由器能上网后,其他设备连接此路由器不能上网
ifconfig -a 看是否有wwan0这个端口
然后修改配置文件
1 | vim /etc/config/network |
添加如下内容:1
2
3
4
5
6
7config interface 'wan'
option device '/dev/cdc-wdm0'
option proto 'qmi'
option apn 'cnnet'
option username 'card'
option password 'card'
option ifname 'wwan0'
登录到路由器web界面,点击网路下的接口,也就是network下的interface,会发现有这么一个设备,点击连接
到此为止,4G路由器就搞定了。
【附】移远提供的拨号程序和参考文档下载地址:https://download.csdn.net/download/hunzhangzui9837/10899023
————————————————
版权声明:本文为CSDN博主「小白clever」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hunzhangzui9837/article/details/85916965