Xavier's Blog

做更美好的自己

EC20

EC204G

EC20 使用

EC20 上网

硬件描述: EC20 模块封装成标准的 PCIe 接口,和系统之家通讯主要通过 usb 通讯。 系统描述: 此处使用的是 Linux 3.0.1。

驱动支持

首先需要对 Linux 内核驱动做一定的修改,使操作系统能够支持 EC20。

Add VID add 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(0x05C6, 0x9215) }, /* Quectel EC20 */
    { USB_DEVICE(0x2C7C, 0x0125) }, /* Quectel EC25/EC20 R2.0 */
    { USB_DEVICE(0x2C7C, 0x0121) }, /* Quectel EC21 */
 #endif
     { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) },

Add the Zero Packet Mechanism

As required by the USB protocol, you need to add the mechanism for processing zero packets during bulkout transmission.

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 *))
{
    struct urb *urb;

    if (endpoint == -1)
        return NULL;    /* endpoint not needed */

    urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
    if (urb == NULL) {
        dbg("%s: alloc for endpoint %d failed.", __func__, endpoint);
        return NULL;
    }

    /* Fill URB using supplied data. */
    usb_fill_bulk_urb(urb, serial->dev,
              usb_sndbulkpipe(serial->dev, endpoint) | dir,
              buf, len, callback, ctx);

 #if 1      // 2017.3.7 add by zhaoxiaodong
    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;
}

Add Reset Resume

Some USB host controllers/USB hubs will lost power or be reset when MCU entering into suspend/sleep mode, and they cannot resume USB devices when MCU exiting from suspend/sleep mode; instead, they will operate reset-resume. You should add the following statements.

For Linux Kernel Version newer than 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
};

For Linux Kernel Version older than 3.5: File: [KERNEL]/drivers/usb/serial/ usb-serial.c

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      // 2017.3.7 add by zhaoxiaodong
    .reset_resume = usb_serial_resume,
 #endif 

    .no_dynamic_id =    1,
    .supports_autosuspend = 1,
};

Modify Kernel Configuration

Step 1:

cd <your kernel directory>

Step 2:

Set your environment variables, and import your board’s defconfig. The following is an example for Raspeberrypi board

export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabimake bcmrpi_defconfig

Step 3:

make menuconfig

Step 4:

Enable CONFIG_USB_SERIAL_OPTION

[*] Device Drivers →
  [*] USB Support →
    [*] USB Serial Converter support →
      [*] USB driver for GSM and CDMA modems

Step 5: Configure Kernel to Support PPP

[*] Device Drivers →
  [*] Network device support →
    [*] PPP (point-to-point protocol) support

5. 编译内核

make

将编译好的内核下载到开发板。

模块测试

将重新编译好的内核下载到开发板之后,待系统重新启动,肉驱动添加正确,在 / dev / 目录下会出现如下设备节点:

/dev/ttyUSB0  /dev/ttyUSB1  /dev/ttyUSB2  /dev/ttyUSB3

EC20 的 AT 端口是 / dev/ttyUSB2,现在你可以使用 UART 端口工具如 “minicom” 或”busybox microcom”测试 AT 功能,结果如下:

#busybox microcom /dev/ttyUSB2
ati;+csub
Quectel
EC20F
Revision: EC20CEFDR02A09M4G

SubEdition: V04

OK

或者

cat /dev/ttyUSB2 & echo -e "AT+CGMI\r\n" >/dev/ttyUSB2
Quectel
OK
AT+CGMI
Quectel
OK
AT+CPIN?

拨号

方案一 通过几个不同的配置文件,在拨号的时候选择相应的配置文件,现将配置文件列举如下: 参考资料 https://linux.die.net/man/8/pppd

/etc/ppp/peers/quectel-ppp

# /etc/ppp/peers/quectel-pppd
# Usage:root>pppd call quectel-pppd
#Modem path, like /dev/ttyUSB3,/dev/ttyACM0, depend on your module, default path is /dev/ttyUSB3
/dev/ttyUSB3 115200
#Insert the username and password for authentication, default user and password are test
user "test" password "test"
# The chat script, customize your APN in this file
connect 'chat -s -v -f /etc/ppp/peers/quectel-chat-connect'
# The close script
disconnect 'chat -s -v -f /etc/ppp/peers/quectel-chat-disconnect'
# Hide password in debug messages
hide-password
# The phone is not required to authenticate
noauth
# Debug info from pppd
debug
# If you want to use the HSDPA link as your gateway
defaultroute
# pppd must not propose any IP address to the peer
noipdefault
# 使能一下两个选项可以实现ppp掉线自动重播
# lcp连接失败尝试次数
lcp-echo-failure 6

# lcp echo发送间隔
lcp-echo-interval 10
# No ppp compression
novj
novjccomp
noccp
ipcp-accept-local
ipcp-accept-remote
local
# For sanity, keep a lock on the serial line
lock
modem
dump
nodetach
# Hardware flow control
nocrtscts
remotename 3gppp
ipparam 3gppp
ipcp-max-failure 30
# Ask the peer for up to 2 DNS server addresses
usepeerdns
# 连接终止后不要退出,而是重新尝试连接,连接次数收到maxfail限制
persist

# 连接最大尝试次数,0无限制
#maxfail

/etc/ppp/peers/quectel-chat-connect

# /etc/ppp/peers/quectel-chat-connect
ABORT "BUSY"
ABORT "NO CARRIER"
ABORT "NO DIALTONE"
ABORT "ERROR"
ABORT "NO ANSWER"
TIMEOUT 30
"" AT
OK ATE0
OK ATI;+CSUB;+CSQ;+CPIN?;+COPS?;+CGREG?;&D2
# Insert the APN provided by your network operator, default apn is 3gnet
OK AT+CGDCONT=1,"IP","3gnet",,0,0
OK ATD*99#
CONNECT

/etc/ppp/peers/quectel-chat-disconnect

# /etc/ppp/peers/quectel-chat-disconnect
ABORT "ERROR"
ABORT "NO DIALTONE"
SAY "\nSending break to the modem\n"
""  +++
""  +++
""  +++
SAY "\nGoodbay\n"

/etc/ppp/peers/quectel-ppp-kill

#!/bin/sh

timeout=5
killall -15 pppd
sleep 1
killall -0 pppd
while [ $? -eq 0 ]
do
    timeout=`expr $timeout - 1`
    if [ $timeout -eq 0 ]
    then
        exit 1
    fi
    sleep 1
    killall -0 pppd
done
    
if [ $? -ne 0 ]
then
    killall -9 pppd
fi

ip-up脚本

之前的脚本如果没有问题,正常的话运行 pppd call gprs 就可以拨号成功,并且ifconfig后可以查看到ppp0这个网卡。但是如果你在拨号之前已经启用了以太网eth0,此时指定使用ppp0网卡ping指定ip或域名ping -I ppp0 www.baidu.com 并不能成功,原因有两个,一个是路由表中的默认网关是之前eth0生成的,一个是默认的dns服务器地址有问题。

解决默认网关问题

在pppd脚本中使能了defaultroute后,pppd会在拨号成功后向路由表中添加一条默认网关信息,但是因为之前已经有一条默认网关了,于是添加失败,默认网关依然是之前eth设置的,因此在pppd call gprs之前我们应该先route del default来删除默认路由(最好写个脚本来实现删除默认路由-拨号等一系列操作)。

或者将ppp0设置为默认路由。

先删除原来路由,route del default 再将ppp0设置为默认路由,route add default dev ppp0

如果不这样做,在我操作中ping 8.8.8.8是ping不通的,要指定网卡才能ping通,ping -I ppp0 8.8.8.8)

解决dns问题

在pppd脚本中使能了usepeerdns后,pppd会在拨号成功后,在/etc/ppp下生成resolv.conf,这是ISP运营商提供的dns,我们应该将拷贝或者连接到/etc目录下,当然这件事就可以交给ip-up来做。

#!/bin/bash
#ip-up
dns_file="/etc/resolv.conf"

rm "$dns_file"
ln /etc/ppp/resolv.conf "$dns_file"
#!/bin/sh

if [ -f /etc/ppp/resolv.conf ]; then
    cp /etc/ppp/resolv.conf /etc/resolv.conf
elif [ -f /var/run/ppp/resolv.conf ]; then
    cp /var/run/ppp/resolv.conf /etc/resolv.conf
else
    echo nameserver $DNS1 > /etc/resolv.conf
    echo nameserver $DNS2 >> /etc/resolv.conf
fi

ip-down脚本

按照ppp拨号过程中打印信息可以发现,在退出pppd进程时,会调用/etc/ppp/ip-down。因此删除默认网关,恢复dns就交由ip-down脚本做了。

#!/bin/bash
#ip-down
#set -vx
dns_file="/etc/resolv.conf"

rm $dns_file

cat > "$dns_file" <<EOF
# auto create by ip-down
nameserver 114.114.114.114
nameserver 8.8.8.8
EOF

chmod 755 "$dns_file"

/etc/init.d/networking restart

echo "Set dns for eth0"

编辑好这几个文件之后,便可以通过 pppd 进行拨号:

# pppd call quectel-ppp &

如果拨号成功会有以下信息打印出来:

[root@zx64352 peers]# pppd call quectel-ppp
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 30             # (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)
AT^M^M
OK
 -- got it

send (ATE0^M)
expect (OK)
^M
ATE0^M^M
OK
 -- got it

send (ATI;+CSUB;+CSQ;+CPIN?;+COPS?;+CGREG?;&D2^M)
expect (OK)
^M
^M
Quectel^M
EC20F^M
Revision: EC20CEFDR02A09M4G^M
^M
SubEdition: V04^M
^M
+CSQ: 14,99^M
^M
+CPIN: READY^M
^M
+COPS: 0^M
^M
+CGREG: 0,0^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 785), status = 0x0
Serial connection established.
using channel 1
Using interface ppp0
Connect: ppp0 <--> /dev/ttyUSB3
sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x1f2b413> <pcomp> <accomp>]
rcvd [LCP ConfReq id=0x0 <asyncmap 0x0> <auth chap MD5> <magic 0xd11eadc2> <pcomp> <accomp>]
sent [LCP ConfAck id=0x0 <asyncmap 0x0> <auth chap MD5> <magic 0xd11eadc2> <pcomp> <accomp>]
rcvd [LCP ConfAck id=0x1 <asyncmap 0x0> <magic 0x1f2b413> <pcomp> <accomp>]
rcvd [LCP DiscReq id=0x1 magic=0xd11eadc2]
rcvd [CHAP Challenge id=0x1 <40aedf97298706e7675d614c0cea9175>, name = "UMTS_CHAP_SRVR"]
sent [CHAP Response id=0x1 <1bf312e7aa43615e474c49c28fbbaca5>, 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=0x0]
sent [IPCP ConfNak id=0x0 <addr 0.0.0.0>]
rcvd [IPCP ConfNak id=0x1 <addr 10.35.149.45> <ms-dns1 218.4.4.4> <ms-dns2 218.2.2.2>]
sent [IPCP ConfReq id=0x2 <addr 10.35.149.45> <ms-dns1 218.4.4.4> <ms-dns2 218.2.2.2>]
rcvd [IPCP ConfReq id=0x1]
sent [IPCP ConfAck id=0x1]
rcvd [IPCP ConfAck id=0x2 <addr 10.35.149.45> <ms-dns1 218.4.4.4> <ms-dns2 218.2.2.2>]
Could not determine remote IP address: defaulting to 10.64.64.64
local  IP address 10.35.149.45
remote IP address 10.64.64.64
primary   DNS address 218.4.4.4
secondary DNS address 218.2.2.2

此时也会在 / etc/ppp / 目录下生成 resolv.conf 文件,存放 pppd 拨号获取到的 DNS,可以将改文件拷贝到 / etc / 目录下,这样便可以进行 DNS 解析了,至此开发板也可以通过 EC20 模块连接到互联网了。

方案二

方案一中的端口号、APN、用户名、密码等都是直接写在配置文件中,这样如果要更换这些内容的话要频繁修改配置文件,既消耗时间,又不太方便。所以便将几个配置文件融合到一个脚本中,并且可以通过传递参数那样修改需要修改的内容,脚本内容如下:

/etc/ppp/peers/quectel-pppd.sh

#!/bin/sh

#quectel-pppd devname apn user password
echo "quectel-pppd options in effect:"
QL_DEVNAME=/dev/ttyUSB3
QL_APN=3gnet
QL_USER=user
QL_PASSWORD=passwd
if [ $# -ge 1 ]; then
    QL_DEVNAME=$1  
    echo "devname   $QL_DEVNAME    # (from command line)"
else
    echo "devname   $QL_DEVNAME    # (default)"
fi
if [ $# -ge 2 ]; then
    QL_APN=$2  
    echo "apn       $QL_APN    # (from command line)"
else
    echo "apn       $QL_APN    # (default)"
fi
if [ $# -ge 3 ]; then
    QL_USER=$3 
    echo "user      $QL_USER   # (from command line)"
else
    echo "user      $QL_USER   # (default)"
fi
if [ $# -ge 4 ]; then
    QL_PASSWORD=$4 
    echo "password  $QL_PASSWORD   # (from command line)"
else
    echo "password  $QL_PASSWORD   # (default)"
fi

CONNECT="'chat -s -v ABORT BUSY ABORT \"NO CARRIER\" ABORT \"NO DIALTONE\" ABORT ERROR ABORT \"NO ANSWER\" TIMEOUT 30 \
\"\" AT OK ATE0 OK ATI\;+CSUB\;+CSQ\;+CPIN?\;+COPS?\;+CGREG?\;\&D2 \
OK AT+CGDCONT=1,\\\"IP\\\",\\\"$QL_APN\\\",,0,0 OK ATD*99# CONNECT'"

pppd $QL_DEVNAME 115200 user "$QL_USER" password "$QL_PASSWORD" \
connect "'$CONNECT'" \
disconnect 'chat -s -v ABORT ERROR ABORT "NO DIALTONE" SAY "\nSending break to the modem\n" "" +++ "" +++ "" +++ SAY "\nGood bay\n"' \
noauth debug defaultroute noipdefault novj novjccomp noccp ipcp-accept-local ipcp-accept-remote ipcp-max-configure 30 local lock modem dump nodetach nocrtscts usepeerdns

拨号

/etc/ppp/peers/quectel-pppd.sh &
``

输出信息跟方案一输出一样,同样会生成 / etc/ppp/resolv.conf 文件。


**注:** 在进行拨号之前一定要确保没有默认网关,如果已经设置了其他网卡的默认网关了,则 4G 模块没法正常上网,需要删除原来的默认网关,现以网卡 eth0 为例:


```sh
route del default
route del -host 255.255.255.255 dev eth0

各个运营商拨号上网设置

运营商(ISP) APN 拨号号码 账号 密码
中国联通WCDMA(China Unicom)) 3GNET *99#
中国电信CDMA2000(China Telecom) #777 ctnet@mycdma.cn vnet.mobi
中国移动 TD-SCDMA(China Mobile) CMNET *98*1#
中国移动 CPRS(China Mobile) CMNET *99***1#

验证模块通讯

设置模块ECHO OFF模式,并在后台持续输出/dev/ttyUSB2设备的信息。 echo -e “ATE0” > /dev/ttyUSB2 cat /dev/ttyUSB2 & echo -e “ATE1” > /dev/ttyUSB2

查看设备ID信息 echo -e “ATI” > /dev/ttyUSB2

查看设备的软件固件版本 echo -e “AT+GMR” > /dev/ttyUSB2

查看设备的IMEI编码 echo -e “AT+GSN” > /dev/ttyUSB2

查询当前SIM卡的运营商 cat /dev/ttyUSB2 & echo -e “AT+COPS?” > /dev/ttyUSB2

查询当前网络的信号质量 cat /dev/ttyUSB2 & echo -e “AT+CSQ” > /dev/ttyUSB2

使用如下命令进行 ppp 拨号(电信卡): sudo /home/pi/quectel-pppd.sh /dev/ttyUSB3 ctnet ctnet@mycdma.cn vnet.mobi 移动卡的拨号命令如下: sudo /home/pi/quectel-pppd.sh /dev/ttyUSB3 cmnet 联通卡的拨号命令如下: sudo /home/pi/quectel-pppd.sh /dev/ttyUSB3 wonet 命令后的三个参数分别是:拨号设备 APN username password