一、USB Serial Driver 安装
当设备连接4G模块后,查看 /dev 下是否有以下设备文件:
ttyUSB0/ttyUSB1/ttyUSB2…
如果没有,则需要按照下列步骤集成USB串行驱动程序(以下操作均需要在内核根目录下进行,因此在操作前要安装对应版本的内核源码):
1.1 添加 VID 和 PID
为了识别模块,客户应该添加模块VID和PID信息,如下所示:
File: [KERNEL]/drivers/usb/serial/option.c
static const 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(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 */
#endif
对于EC20模块,如果内核源文件中存在以下文件和语句,请删除它们,因为它们会与EC20的USB驱动程序冲突。
File: [KERNEL]/drivers/usb/serial/qcserial.c
{USB_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
File: [KERNEL]/drivers/net/usb/qmi_wwan.c
{QMI_GOBI_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
1.2 添加零分组机制
根据USB协议的要求,客户需要在大量传输过程中添加用于处理零数据包的机制。
对于高于2.6.34的Linux内核版本:
File: [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(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;
}
对于Linux内核版本低于2.6.35:
File: [KERNEL]/drivers/usb/serial/option.c
/* Helper functions used by option_setup_urbs */
static struct urb *option_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(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;
}
1.3 添加重置恢复
一些USB主机控制器/USB集线器将在MCU进入暂停/睡眠模式时损失或重置,并且在MCU从暂停/睡眠模式下退出后,它们无法恢复USB设备。请添加以下语句以启用重置恢复过程。
对于Linux内核版本高于3.4:
File: [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
};
对于Linux内核版本低于3.5:
File: [KERNEL]/drivers/usb/serial/ usb-serial.c
/* Driver structure we register with the USB core */
static struct usb_driver usb_serial_driver = {
.name = "usbserial",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.suspend = usb_serial_suspend,
.resume = usb_serial_resume,
#if 1 //Added by Quectel
.reset_resume = usb_serial_resume,
#endif
.no_dynamic_id = 1,
.supports_autosuspend = 1,
};
1.4 放大散装网址
对于低于2.6.29的Linux内核版本,大量URB需要放大以获得更快的上行链路速度。
File: [KERNEL]/drivers/usb/serial/option.c
#define N_IN_URB 4
#define N_OUT_URB 4 //Quectel 1
#define IN_BUFLEN 4096
#define OUT_BUFLEN 4096 //Quectel 128
1.5 使用 GobiNet 或 QMI WWAN
如果要求使用Gobinet或QMI WWAN,请添加以下语句以防止这些模块的接口4作为USB串行设备。
对于Linux内核版本高于2.6.30:
File: [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
//Quectel UC20's interface 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 interface 4 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;
//Quectel EC25&EC21&EG91&EG95&EG06&EP06&EM06&BG96/AG35'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 during attach. */
usb_set_serial_data(serial, (void *)id);
return 0;
}
对于Linux内核版本低于2.6.31:
File: [KERNEL]/drivers/usb/serial/option.c
static int option_startup(struct usb_serial *serial)
{
……
dbg("%s", __func__);
#if 1 //Added by Quectel
//Quectel UC20's interface 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 interface 4 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;
//Quectel EC25&EC21&EG91&EG95&EG06&EP06&EM06&BG96/AG35'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
……
}
1.6 修改内核配置
内核配置中有几个强制性选定项目,请按照以下步骤配置内核:
步骤1:进入到内核根目录。
cd < your kernel directory >
步骤2:设置环境变量和导入板的defconfig。以下是RaspeBerry Pi板的示例。
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabi-
make bcmrpi_defconfig
对于X86架构的系统,我的默认的defconfig文件为x86_64_defconfig,只需要在内核根目录下 make x86_64_defconfig
步骤3:编译内核。
make menuconfig
步骤4:启用CONFIG_USB_SERIAL_OPTION
[*] Device Drivers →
[*] USB Support →
[*] USB Serial Converter support →
[*] USB driver for GSM and CDMA modems
1.7 作为内核模块构建并加载驱动程序
请按照以下步骤将驱动程序作为内核模块构建。
步骤1:进入到内核根目录。
cd < your kernel directory >
步骤2:构建驱动程序。
sudo make -C /lib/modules/`uname -r`/build M=`pwd`/drivers/usb/serial obj-m=option.o modules
sudo make -C /lib/modules/`uname -r`/build M=`pwd`/drivers/usb/serial obj-m=usb_wwan.o modules
sudo make -C /lib/modules/`uname -r`/build M=`pwd`/drivers/usb/serial obj-m=qcserial.o modules
步骤3:加载驱动程序并重新启动。
sudo cp drivers/usb/serial/option.ko /lib/modules/`uname -r`/kernel/drivers/usb/serial
sudo cp drivers/usb/serial/usb_wwan.ko /lib/modules/`uname -r`/kernel/drivers/usb/serial
sudo cp drivers/usb/serial/qcserial.ko /lib/modules/`uname -r`/kernel/drivers/usb/serial
sudo depmod
sudo reboot
二、配置内核以支持PPP
首先检测 /dev 下面是否有 ppp 设备,没有的话请按照以下步骤配置内核以支持PPP。
步骤1:进入到内核根目录。
cd < your kernel directory >
步骤2:设置环境变量和导入板的defconfig。下面显示了一个例子。
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabi-
make bcmrpi_defconfig
对于X86架构的系统,我的默认的defconfig文件为x86_64_defconfig,只需要在内核根目录下 make x86_64_defconfig
步骤3:编译内核。
make menuconfig
步骤4:启用CONFIG_PPP_ASYNC CONFIG_PPP_SYNC_TTY CONFIG_PPP_DEFLATE
[*] Device Drivers →
[*] Network device support →
[*] PPP (point-to-point protocol) support
内核启动后在 /dev 下生成 ppp 设备节点
三、测试PPP拨号上网
下载ppp服务:https://ppp.samba.org/download.html
解压:tar -xzvf ppp-2.4.4.tar.gz
cd ppp-2.4.4
./configure
make && make install
实现ppp拨号上网:
查看是否有名为/etc/ppp/ip-up
的ppp脚本文件,用于设置DNS(域名系统)。如果没有这样的文件,请使用 Quectel 提供的linux-ppp-scripts\ip-up
。
关于 linux-ppp-scripts_V1.2.zip 请自行到网上下载。
解压:unzip linux-ppp-scripts_V1.2.zip
将 linux-ppp-scripts
里面的 quectel-ppp
,quectel-chat-connect
,quectel-chat-disconnect
三个脚本复制到 /etc/ppp/peers
目录下
根据不同的模块,客户可能需要进行一些更改。有关更多信息,请参阅 linux-ppp-scripts\readme
。
然后通过以下命令开始设置PPP呼叫:
pppd call quectel-ppp &
拨号过程如下所示(EC20的示例):
# pppd options in effect:
debug # (from /etc/ppp/peers/quectel-ppp)
nodetach # (from /etc/ppp/peers/quectel-ppp)
dump # (from /etc/ppp/peers/quectel-ppp)
noauth # (from /etc/ppp/peers/quectel-ppp)
user test # (from /etc/ppp/peers/quectel-ppp)
password ?????? # (from /etc/ppp/peers/quectel-ppp)
remotename 3gppp # (from /etc/ppp/peers/quectel-ppp)
/dev/ttyUSB3 # (from /etc/ppp/peers/quectel-ppp)
115200 # (from /etc/ppp/peers/quectel-ppp)
lock # (from /etc/ppp/peers/quectel-ppp)
connect chat -s -v -f /etc/ppp/peers/quectel-chat-connect # (from
/etc/ppp/peers/quectel-ppp)
disconnect chat -s -v -f /etc/ppp/peers/quectel-chat-disconnect # (from
/etc/ppp/peers/quectel-ppp)
nocrtscts # (from /etc/ppp/peers/quectel-ppp)
modem # (from /etc/ppp/peers/quectel-ppp)
hide-password # (from /etc/ppp/peers/quectel-ppp)
novj # (from /etc/ppp/peers/quectel-ppp)
novjccomp # (from /etc/ppp/peers/quectel-ppp)
ipcp-accept-local # (from /etc/ppp/peers/quectel-ppp)
ipcp-accept-remote # (from /etc/ppp/peers/quectel-ppp)
ipparam 3gppp # (from /etc/ppp/peers/quectel-ppp)
noipdefault # (from /etc/ppp/peers/quectel-ppp)
ipcp-max-failure 10 # (from /etc/ppp/peers/quectel-ppp)
defaultroute # (from /etc/ppp/peers/quectel-ppp)
usepeerdns # (from /etc/ppp/peers/quectel-ppp)
noccp # (from /etc/ppp/peers/quectel-ppp)
abort on (BUSY)
abort on (NO CARRIER)
abort on (NO DIALTONE)
abort on (ERROR)
abort on (NO ANSWER)
timeout set to 30 seconds
send (AT^M)
expect (OK)
^M
OK
-- got it
send (ATE0^M)
expect (OK)
^M
^M
OK
-- got it
send (ATI;+CSUB;+CSQ;+CPIN?;+COPS?;+CGREG?;&D2^M)
expect (OK)
^M
^M
Quectel^M
EC20^M
Revision: EC20CQAR02A03E2G_BETA0914^M
^M
SubEdition: V01^M
^M
+CSQ: 23,99^M
^M
+CPIN: READY^M
^M
+COPS: 0,0,"CHN-CT",7^M
^M
+CGREG: 2,1,"FFFE","6916934",7^M
^M
OK
-- got it
send (AT+CGDCONT=1,"IP","3gnet",,0,0^M)
expect (OK)
^M
^M
OK
-- got it
send (ATD*99#^M)
expect (CONNECT)
^M
^M
CONNECT
-- got it
Script chat -s -v -f /etc/ppp/peers/quectel-chat-connect finished (pid 3017), status = 0x0
Serial connection established.
using channel 3
Using interface ppp0
Connect: ppp0 <--> /dev/ttyUSB3
sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0xf2b7d6ee> <pcomp> <accomp>]
rcvd [LCP ConfReq id=0x4 <asyncmap 0x0> <auth chap MD5> <magic 0x45c0e381> <pcomp>
<accomp>]
sent [LCP ConfAck id=0x4 <asyncmap 0x0> <auth chap MD5> <magic 0x45c0e381> <pcomp>
<accomp>]
rcvd [LCP ConfAck id=0x1 <asyncmap 0x0> <magic 0xf2b7d6ee> <pcomp> <accomp>]
rcvd [LCP DiscReq id=0x5 magic=0x45c0e381]
rcvd [CHAP Challenge id=0x1 <f8d54e0fa294c100101805a512176ff1>, name =
"UMTS_CHAP_SRVR"]
sent [CHAP Response id=0x1 <e8ad86182138523599fb54a172da7154>, name = "test"]
rcvd [CHAP Success id=0x1 ""]
CHAP authentication succeeded
CHAP authentication succeeded
sent [IPCP ConfReq id=0x1 <addr 0.0.0.0> <ms-dns1 0.0.0.0> <ms-dns2 0.0.0.0>]
rcvd [IPCP ConfReq id=0x4]
sent [IPCP ConfNak id=0x4 <addr 0.0.0.0>]
rcvd [IPCP ConfNak id=0x1 <addr 100.65.245.137> <ms-dns1 61.132.163.68> <ms-dns2
202.102.213.68>]
sent [IPCP ConfReq id=0x2 <addr 100.65.245.137> <ms-dns1 61.132.163.68> <ms-dns2
202.102.213.68>]
rcvd [IPCP ConfReq id=0x5]
sent [IPCP ConfAck id=0x5]
rcvd [IPCP ConfAck id=0x2 <addr 100.65.245.137> <ms-dns1 61.132.163.68> <ms-dns2
202.102.213.68>]
Could not determine remote IP address: defaulting to 10.64.64.64
local IP address 100.65.245.137
remote IP address 10.64.64.64
primary DNS address 61.132.163.68
secondary DNS address 202.102.213.68
Script /etc/ppp/ip-up started (pid 3020)
Script /etc/ppp/ip-up finished (pid 3020), status = 0x0
现在PPP呼叫设置成功。通过以下方式查看:
ifconfig ppp0
ppp0 Link encap:Point-to-Point Protocol
inet addr:100.65.245.137 P-t-P:10.64.64.64 Mask:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1
RX packets:15 errors:0 dropped:0 overruns:0 frame:0
TX packets:19 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:3
RX bytes:1057 (1.0 KiB) TX bytes:1228 (1.1 KiB)
cat /etc/resolv.conf
nameserver 61.132.163.68
nameserver 202.102.213.68
route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.64.64.64 0.0.0.0 255.255.255.255 UH 0 0 0 ppp0
0.0.0.0 0.0.0.0 0.0.0.0 U 0 0 0 ppp0
ping www.baidu.com
PING www.a.shifen.com (115.239.211.112) 56(84) bytes of data.
64 bytes from 115.239.211.112: icmp_seq=1 ttl=54 time=46.4 ms
关闭PPP呼叫:
pkill -f pppd
参考文献:
《Quectel_WCDMA<E_Linux_USB_Driver_User_Guide_V1.8.pdf》