Openwrt支持移遠4G模塊過程記錄


Openwrt支持移遠4G模塊過程記錄

來源 https://www.right.com.cn/FORUM/thread-4033702-1-1.html

參考  http://iotts.com.cn/blog/2021/08/08/OpenWrt-%E6%8C%82%E8%BD%BD%E7%A7%BB%E8%BF%9CEC20%E3%80%81EC21%E3%80%81EC25%E3%80%81AG35%E7%AD%894G%E6%A8%A1%E5%9D%97/

 

參考了不少教程,也做了不少嘗試,最后記錄下整個過程以便自己和壇友們參考
我所用的4G模塊是Quectel EC20 R2.0,對於這個型號以下的操作前半部分修改USB Serial驅動應該是不需要的,但是我也都修改了並且記錄下來,對於其他品牌型號的模塊應該是原理相通的

修改內核增加USB Serial驅動支持
1.增加PID&VID,教程中所用內核4.4.79尚沒有支持,我用的4.19.122已經支持,而且兩者之間的定義方式稍有不同
教程里需要添加的代碼為
build_dir/target-mips_24kc_musl/linux-ar71xx_generic/linux-4.4.79/drivers/usb/serial/option.c
【注】只添加#if 1 到 #endif的內容
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
可以看到是在結構體usb_device_id option_ids[]里添加了移遠各型號的PID和VID
而新內核的此結構體是通過在前面define然后后面通過VENDOR_ID和PRODUCT來定義
#define QUECTEL_VENDOR_ID                                0x2c7c
/* These Quectel products use Quectel's vendor ID */
#define QUECTEL_PRODUCT_EC21                        0x0121
#define QUECTEL_PRODUCT_EC25                        0x0125
#define QUECTEL_PRODUCT_BG96                        0x0296
#define QUECTEL_PRODUCT_EP06                        0x0306
#define QUECTEL_PRODUCT_EM12                        0x0512
#define QUECTEL_PRODUCT_RM500Q                        0x0800
后面則是
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25), .driver_info = RSVD(4) },
總之就是此處不需要額外修改了

2.添加零包處理
文件位於build_dir/target-mips_24kc_musl/linux-ar71xx_generic/linux-4.4.79/drivers/usb/serial/usb_wwan.c
添加在函數urb中
static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
                                      int endpoint,
                                      int dir, void *ctx, char *buf, int len,
                                      void (*callback) (struct urb *))
{
        struct usb_serial *serial = port->serial;
        struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
        struct urb *urb;

        urb = usb_alloc_urb(0, GFP_KERNEL);        /* No ISO */
        if (!urb)
                return NULL;

        usb_fill_bulk_urb(urb, serial->dev,
                          usb_sndbulkpipe(serial->dev, endpoint) | dir,
                          buf, len, callback, ctx);

        if (intfdata->use_zlp && dir == USB_DIR_OUT)
                urb->transfer_flags |= URB_ZERO_PACKET;

#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;
                if (desc->idVendor == cpu_to_le16(0x1286) && desc->idProduct == cpu_to_le16(0x4e3d))
                {
                    urb->transfer_flags |= URB_ZERO_PACKET;
                }
            }
#endif
        return urb;
}

3.增加休眠后喚醒接口
文件位於build_dir/target-mips_24kc_musl/linux-ar71xx_generic/linux-4.4.79/drivers/usb/serial/option.c
添加在option_1port_device結構體中

static struct usb_serial_driver option_1port_device = {
        .driver = {
                .owner =        THIS_MODULE,
                .name =                "option1",
        },
        .description       = "GSM modem (1-port)",
        .id_table          = option_ids,
        .num_ports         = 1,
        .probe             = option_probe,
        .open              = usb_wwan_open,
        .close             = usb_wwan_close,
        .dtr_rts           = usb_wwan_dtr_rts,
        .write             = usb_wwan_write,
        .write_room        = usb_wwan_write_room,
        .chars_in_buffer   = usb_wwan_chars_in_buffer,
        .tiocmget          = usb_wwan_tiocmget,
        .tiocmset          = usb_wwan_tiocmset,
        .ioctl             = usb_wwan_ioctl,
        .attach            = option_attach,
        .release           = option_release,
        .port_probe        = usb_wwan_port_probe,
        .port_remove           = usb_wwan_port_remove,
        .read_int_callback = option_instat_callback,
#ifdef CONFIG_PM
        .suspend           = usb_wwan_suspend,
        .resume            = usb_wwan_resume,

#if 1 //Added by Quectel
        .reset_resume      = usb_wwan_resume,
#endif
#endif
};

4.使用 GobiNet or QMI WWAN
如果使用ucxx/ec2x/egxx/EP06/EM06/BG96/AG35,並要求GobiNet或QMI WWAN,請添加以下語句,以防止這些模塊接口4被用作USB串行設備
文件位於build_dir/target-mips_24kc_musl/linux-ar71xx_generic/linux-4.4.79/drivers/usb/serial/option.c
添加在函數option_probe中

static int option_probe(struct usb_serial *serial,
                        const struct usb_device_id *id)
{
        struct usb_interface_descriptor *iface_desc =
                                &serial->interface->cur_altsetting->desc;
        unsigned long device_flags = id->driver_info;

        /* Never bind to the CD-Rom emulation interface        */
        if (iface_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE)
                return -ENODEV;

        /*
         * Don't bind reserved interfaces (like network ones) which often have
         * the same class/subclass/protocol as the serial interfaces.  Look at
         * the Windows driver .INF files for reserved interface numbers.
         */
        if (device_flags & RSVD(iface_desc->bInterfaceNumber))
                return -ENODEV;

        /*
         * Allow matching on bNumEndpoints for devices whose interface numbers
         * can change (e.g. Quectel EP06).
         */
        if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2)
                return -ENODEV;

#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 the device flags so we can use them during attach. */
        usb_set_serial_data(serial, (void *)device_flags);

        return 0;
}

以下部分則是必須的了,不然即便設備和接口都能正常識別,但是卻沒法獲取ip,此處參考https://www.right.com.cn/forum/thread-480555-1-1.html

修改內核增加QMI WWAN驅動支持
1.增加VID和PID
文件位於build_dir/target-mips_24kc_musl/linux-ar71xx_generic/linux-4.4.79/drivers/net/usb/qmi_wwan.c
在結構體usb_device_id products[]中
static const struct usb_device_id products[] = {

#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(0x2C7C, 0x0125, 4) }, /* Quectel EC25 */
        { QMI_FIXED_INTF(0x2C7C, 0x0121, 4) }, /* Quectel EC21 */
        { QMI_FIXED_INTF(0x05C6, 0x9215, 4) }, /* Quectel EC20 */
        { QMI_FIXED_INTF(0x2C7C, 0x0191, 4) }, /* Quectel EG91 */
        { QMI_FIXED_INTF(0x2C7C, 0x0195, 4) }, /* Quectel EG95 */
        { QMI_FIXED_INTF(0x2C7C, 0x0306, 4) }, /* Quectel EG06/EP06/EM06 */
        { QMI_FIXED_INTF(0x2C7C, 0x0296, 4) }, /* Quectel BG96 */
        { QMI_FIXED_INTF(0x2C7C, 0x0435, 4) }, /* Quectel AG35 */
#endif

但是我注意到源文件里有
#define QMI_MATCH_FF_FF_FF(vend, prod) \
        USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_VENDOR_SPEC, \
                                      USB_SUBCLASS_VENDOR_SPEC, 0xff), \
        .driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr
...
...
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)},        /* Quectel EC25, EC20 R2.0  Mini PCIe */
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)},        /* Quectel EP06/EG06/EM06 */
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)},        /* Quectel EG12/EM12 */
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)},        /* Quectel RM500Q-GL */
不知道此處應該怎么理解

2.增加對Raw IP Mode的支持
文件位於build_dir/target-mips_24kc_musl/linux-ar71xx_generic/linux-4.4.79/drivers/net/usb/qmi_wwan.c
增加一個函數
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;
};
本來教程還要添加一部分LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 )也就是處理內核版本低於3.9.1的,因為已知肯定是高於這個版本了,我就沒有添加了
下來是修改qmi_wwan_bind函數
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
{
        int status = -1;
        u8 *buf = intf->cur_altsetting->extra;
        int len = intf->cur_altsetting->extralen;
...
...
        /* 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 */
        }
        dev->net->netdev_ops = &qmi_wwan_netdev_ops;
        dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group;

#if 1 //Added by Quectel
        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;
                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);
        }
#endif
err:
        return status;
}
教程里還有一個要修改的函數qmi_wwan_bind_shared,但是在此處不存在,所以不修改
然后是兩個結構體qmi_wwan_info和qmi_wwan_info_quirk_dtr里需要添加
static const struct driver_info        qmi_wwan_info = {
        .description        = "WWAN/QMI device",
        .flags                = FLAG_WWAN | FLAG_SEND_ZLP,
        .bind                = qmi_wwan_bind,
        .unbind                = qmi_wwan_unbind,
        .manage_power        = qmi_wwan_manage_power,
        .rx_fixup       = qmi_wwan_rx_fixup,

#if 1 //Added by Quectel
        .tx_fixup       = qmi_wwan_tx_fixup,
#endif
};

static const struct driver_info        qmi_wwan_info_quirk_dtr = {
        .description        = "WWAN/QMI device",
        .flags                = FLAG_WWAN | FLAG_SEND_ZLP,
        .bind                = qmi_wwan_bind,
        .unbind                = qmi_wwan_unbind,
        .manage_power        = qmi_wwan_manage_power,
        .rx_fixup       = qmi_wwan_rx_fixup,

#if 1 //Added by Quectel
        .tx_fixup       = qmi_wwan_tx_fixup,
#endif
        .data           = QMI_WWAN_QUIRK_DTR,
};

編譯設置
內核

    • Device Drivers ->
                     
    • USB Support ->
                             
    • USB Serial Converter support ->
                                     
    • USB driver for GSM and CMDA modems
    • Device Drivers →
                      -*- Network device support →
                                      USB Network Adapters →
                                              {*} Multi-purpose USB Networking Framework
                                                      <*> QMI WWAN driver for Qualcomm MSM based 3G and LTE modems

      常規
      Kernel modules >>
              USB Support >>
                      <*> Kmod -usb-core
                      -*-   Kmod -usb-net
                      -*-   Kmod-usb-net-cdc-ether
                      <*> Kmod-usb-net-cdc-mbim
                      -*-   Kmod-usb-net-cdc-ncm
                      <*> Kmod-usb-net-cdc-subset
                      <*> Kmod-usb-net-qmi-wwan
                      <*> Kmod-usb-ohci
                      <*> Kmod-usb-serial
                      <*> Kmod-usb-serial-option
                      <*> Kmod-usb-uhci
                      <*> Kmod-usb2
      NetWork   >>
              <*>wwan
              <*>chat
              <*>ppp
              <*>umbim
      Utilities
              <*> quectel_cm
              <*> usb-modeswitch
      Luci
      1. Collections
                        <*> luci
      3. Applications
                        <*> luci-app-multiwan (optional to support multiple 3g dongles)
                        <*> luci-app-qos (optional to provide QOS support)
      6. Protocols
                        <*> luci-proto-3g
                        -*-   luci-proto-ppp
                        <*> luci-proto-qmi
      其中quectel_cm也是來自https://download.csdn.net/download/jianzhizuo/10749215因為Quectel對於資料方面的管控比較嚴格,這方面我就不私自放出了,不過我查了github上有哈哈哈

      編譯好刷入后,在ssh執行quectel_cm &就會開始撥號,並且四海眾聯何錦明發布的源碼是會自動設置dns的,不然還要自己手動設置


      撥號成功后ifconfig會看到wwan0這個接口已經有了ip

      然后開始ping baidu.com也有結果了

      到了這時候,路由器自身已經可以上網了,但是還沒能把網絡轉發給LAN,需要額外創建一個接口,叫什么無所謂,協議選DHCP,接口選wwan0,防火牆選在wan就可以了
       <ignore_js_op>
      其他相關內容,也是在學習過程中發現的:
      采用的模塊型號為移遠EC20 R2.0等同於EC25,兩者PID和VID一致,所以在lsusb時識別為EC25
      插上后/dev會出現五個設備,分別是cdc-wdm0,ttyUSB0,ttyUSB1,ttyUSB2,ttyUSB3
      其中cdc-wdm0是進行qmi撥號的設備
      ttyUSB0是DM接口
      ttyUSB1是GPS NMEA信息輸出接口
      ttyUSB2是AT指令的接口
      ttyUSB3是建立ppp連接或者AT指令的接口

      因為ttyUSB2是用來交互AT指令的,所以可以通過cat /dev/ttyUSB2 & 指令來保持這個接口的輸出反饋,然后輸入AT指令來控制模塊
      echo -e "AT+QCCID\r\n" >/dev/ttyUSB2    //查詢SIM卡
      echo -e "AT+CFUN=1,1\r\n" >/dev/ttyUSB2   //重啟模塊
      echo -e "AT+QSIMDET=1\r\n" >/dev/ttyUSB2   //開啟SIM卡熱插拔狀態報告
      echo -e "AT+QSIMDET=1,1\r\n" >/dev/ttyUSB2  //開啟SIM卡檢測功能
      echo -e "AT+CSQ\r\n" >/dev/ttyUSB2  //查詢信號強度
      echo -e "AT+QNWINFO\r\n" >/dev/ttyUSB2 //查詢網絡狀態
      echo -e "AT+CGMM\r\n" >/dev/ttyUSB2   //輸出模塊型號
      echo -e "AT+CGMI\r\n" >/dev/ttyUSB2   //輸出廠商
      echo -e "AT+QCFG=\"NWSCANMODE\",3,1\r\n" >/dev/ttyUSB2 //設置4G LTE only
      echo -e "AT+QCFG=\"NWSCANMODE\",2,1\r\n" >/dev/ttyUSB2 //設置3G WCDMA only
      echo -e "AT+QCFG=\"NWSCANMODE\",1,1\r\n" >/dev/ttyUSB2 //設置2G GSM only
      echo -e "AT+QCFG=\"NWSCANMODE\",0,1\r\n" >/dev/ttyUSB2 //設置自動搜索網絡

 

添加接口並且綁定域為WAN可以這樣搞:
uci set network.GSM=interface
uci set network.GSM.proto=dhcp
uci set network.GSM.ifname=wwan0
uci commit network

uci set firewall.@zone[1].network='wan wan6 GSM'
uci commie firewall

 

QCA4028軟件平台啟用雙WAN指導

來源 https://www.cnblogs.com/gierwu-wirelessIoT/p/8572871.html

 1 為何要啟用雙WAN

  QCA4028的硬件方案,基板上部署了一個LTE模塊插槽,同時又外留了一個USB3.0接口,因此,就可以在此硬件平台上調試基於LTE的雙WAN,預期實現:

A 鏈路備份,在任意一個LTE網絡連接異常時,其下的有線/WiFi無線用戶仍能持續接入上游網絡;

B 負載均衡,盡量讓上行流量均衡分配在2個LTE網絡鏈路上,避免流量其中到單個LTE鏈路。

  本文檔中采用LTE模塊為上海移遠的EC2X模塊,USB3.0的LTE模塊轉接板來自T寶,它自帶了SIM卡槽。

2 具體操作

  在QSDK軟件工程代碼中選中"muliiwan"組件,作為雙wan的監護進程,它缺省是不啟用的,非常好調試。在確保2個LTE模塊都能成功保證WiFi用戶上網后,再來調試雙WAN功能更加有效。本次調試中發現EC系列的模塊,它的wwanX接口的MAC地址是隨機的,每次都不一樣,此外,2個wwan接口的MAC地址還是一樣的,開始調不通時為這個折騰了好久,后來才發現MAC一樣對用戶上網也沒有關系(這個問題可能會埋伏了深層BUG,有待持續改進)。

  mutliwan在同QSDK的配合中,發現提取不到網卡的IP地址和網關地址,導致錯誤將2個WAN鏈路都定位為offline,從而會出現將resolv.conf.auto清空,將default gateway條目刪除掉,導致無法上網。規避方法是:在acquire_wan_data中,在ipaddr和gateway都為x后,再直接到網卡上取ip和gw值。WiFi用戶上網時,會發現打不開頁面或網站的情況,這個是因為QSDK固件中用了dnsmasq,而它需要真實的DNS配合進行解析,refresh_dns中頻繁地清空resolv.conf.auto文件,可能會導致dnsmasq工作異常,規避方法是:不讓refresh_dns頻繁操作resolv.conf.auto,而改為操作一個新臨時文件,只有當這個新臨時文件不為空,且與現有的resolv.conf.auto不同時,再將新臨時文件替換resolv.conf.auto,並重啟dnsmasq,這樣就穩定了。此外,針對monitor_wan,其現有實現對LTE網絡是不適合的,因為LTE網絡可能已經斷開了,但IP和網關卻依舊存在的,所以,這里需要讓它每次都進入add_task,要不在人工將某個wan口down下來后,監控進程都不處理,導致WIFi用戶上網失敗。

  另一方面,就是WAN口的up需要重新優化,在QSDK中,如果不啟用multiwan服務,則缺省路由是后啟動網卡所對應的缺省路由,且只有一條缺省路由條目,導致你將這個唯一的缺省路由網卡關閉后,WiFi用戶就上不了網。這個不是multiwan引入,但也需要規避。可以在定期任務中檢查route條目來解決,檢查缺省路由條目和帶UH標識條目,確保他倆一樣即可。但multiwan確實還是引入了問題的,其一:運行中關閉multiwan服務后,會將default路由條目刪除掉,導致用戶無法上網;其二:WAN接口上線成功后,不會更新dns和缺省路由條目,但它明明檢測了新wan口,且狀態是online了。規避方法是:在multiwan被stop后,重構路由條目(也可在定時任務中檢測路由條目);在網卡更新IP后,立即重啟multiwan服務。但重啟multiwan服務它會重構路由表,可能會導致ping包時延波動或丟包,游戲用戶可能會影響體驗。

3 驗證結果

  • 路由信息表

  

  • 頁面顯示雙WAN正常

 

  • 負載均衡顯示有效

4 結論

  利用Multiwan來實現雙WAN管控是可行的,具體使用時,還需要為目標環境進行簡單修改,可能無法做到拿來即可用。

 

============= End

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM