Openwrt支援移遠4G模組過程記錄
Openwrt支援移遠4G模組過程記錄
來源 https://www.right.com.cn/FORUM/thread-4033702-1-1.html
參考了不少教程,也做了不少嘗試,最後記錄下整個過程以便自己和壇友們參考
我所用的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,此處參考
修改核心增加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也是來自
編譯好刷入後,在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
============= End