确认硬件是否正常,使用busybox lsusb查看
USB Serial Driver
Add VID and PID
Add Reset Resume
Enlarge Bulk out URBs
Use GobiNet or QMI WWAN
File: [KERNEL]/drivers/usb/serial/option.c
root@virtual-machine:/linux-3.10.108/drivers/usb/serial# diff option.c /linux-3.10.x/drivers/usb/serial/option.c
277d276
< #define TELIT_PRODUCT_LE910_USBCFG4 0x1206
379,382c378,380
< /* Gemalto's Cinterion products (formerly Siemens) */
< #define SIEMENS_VENDOR_ID 0x0681
< #define CINTERION_VENDOR_ID 0x1e2d
< #define CINTERION_PRODUCT_HC25_MDMNET 0x0040
---
> /* Cinterion (formerly Siemens) products */
> #define SIEMENS_VENDOR_ID 0x0681
> #define CINTERION_VENDOR_ID 0x1e2d
384c382
< #define CINTERION_PRODUCT_HC28_MDMNET 0x004A /* same for HC28J */
---
> #define CINTERION_PRODUCT_HC25_MDMNET 0x0040
385a384
> #define CINTERION_PRODUCT_HC28_MDMNET 0x004A /* same for HC28J */
391,394d389
< #define CINTERION_PRODUCT_PH8_2RMNET 0x0082
< #define CINTERION_PRODUCT_PH8_AUDIO 0x0083
< #define CINTERION_PRODUCT_AHXX_2RMNET 0x0084
< #define CINTERION_PRODUCT_AHXX_AUDIO 0x0085
649,652d643
< static const struct option_blacklist_info cinterion_rmnet2_blacklist = {
< .reserved = BIT(4) | BIT(5),
< };
<
653a645,656
> #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
1210,1211d1212
< { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
< .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
1726,1732c1727
< { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_2RMNET, 0xff),
< .driver_info = (kernel_ulong_t)&cinterion_rmnet2_blacklist },
< { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_AUDIO, 0xff),
< .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
< { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) },
< { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) },
< { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
---
> { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
1838,1839d1832
< { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
< .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
1844a1838
>
1879a1874,1876
> #if 1 //Added by Quectel
> .reset_resume = usb_wwan_resume,
> #endif
1946,1947c1943,2000
<
< /* Store device id so we can use it during attach. */
---
> #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
> #if 1 //Added by Quectel
> //For USB Auto Suspend
> if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) &&
> serial->dev->descriptor.idProduct == cpu_to_le16(0x9090)) {
> pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000);
> usb_enable_autosuspend(serial->dev);
> }
> if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) &&
> serial->dev->descriptor.idProduct == cpu_to_le16(0x9003)) {
> pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000);
> usb_enable_autosuspend(serial->dev);
> }
> if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) &&
> serial->dev->descriptor.idProduct == cpu_to_le16(0x9215)) {
> pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000);
> usb_enable_autosuspend(serial->dev);
> }
> if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
> pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000);
> usb_enable_autosuspend(serial->dev);
> }
> #endif
> #if 1 //Added by Quectel
> //For USB Remote Wakeup
> if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) &&
> serial->dev->descriptor.idProduct == cpu_to_le16(0x9090)) {
> device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup
> }
> if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) &&
> serial->dev->descriptor.idProduct == cpu_to_le16(0x9003)) {
> device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup
> }
> if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) &&
> serial->dev->descriptor.idProduct == cpu_to_le16(0x9215)) {
> device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup
> }
> if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
> device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup
> }
> #endif
> /* Store device id so we can use it during attach. */
Add the Zero Packet Mechanism
File: [KERNEL]/drivers/usb/serial/usb_wwan.c
root@virtual-machine:/linux-3.10.108/drivers/usb/serial# diff usb_wwan.c /linux-3.10.x/drivers/usb/serial/usb_wwan.c
492,493c492,507
< usb_sndbulkpipe(serial->dev, endpoint) | dir,
< buf, len, callback, ctx);
---
> 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) && desc->idProduct == cpu_to_le16(0x0125))
> urb->transfer_flags |= URB_ZERO_PACKET;
> }
> #endif
Add Reset Resume
File: [KERNEL]/drivers/usb/serial/ usb-serial.c
root@virtual-machine: /linux-3.10.108/drivers/usb/serial# diff usb-serial.c /linux-3.10.x/drivers/usb/serial/usb-serial.c
1447c1447
< goto failed_usb_register;
---
> return rc;
1465,1466d1464
< failed_usb_register:
< kfree(udriver);
QMI WWAN Driver
Add VID and PID
Add Support for Raw IP Mode
[KERNEL]/drivers/net/usb/qmi_wwan.c
root@yhj-virtual-machine:/home/yhj/sky9151/linux-3.10.108/drivers/net/usb# diff qmi_wwan.c /home/yhj/sky9151/linux-3.10.x/drivers/net/usb/qmi_wwan.c
21a22,81
> #if 1 //Added by Quectel
> #include <linux/etherdevice.h>
> 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;
> }
> #include <linux/version.h>
> #if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 ))
> 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;
> }
> #endif
> #endif
334a395,416
> #if 1 //Added by Quectel
> if (dev->udev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
> printk("Quectel EC25&EC21&EG91&EG95&EG06&EP06&EM06&BG96&AG35 work on RawIP mode\n");
> dev->net->flags |= IFF_NOARP;
> #if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 ))
> /* 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 */
> }
> #endif
> 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
>
337a420,446
> static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf)
> {
> int status = -1;
> #if 1 //Added by Quectel
> if (dev->udev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
> printk("Quectel EC25&EC21&EG91&EG95&EG06&EP06&EM06&BG96&AG35 work on RawIP mode\n");
> dev->net->flags |= IFF_NOARP;
> #if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 ))
> /* 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 */
> }
> #endif
> 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;
> }
418a528,543
> #if 1 //Added by Quectel
> .tx_fixup = qmi_wwan_tx_fixup,
> .rx_fixup = qmi_wwan_rx_fixup,
> #endif
> };
> static const struct driver_info qmi_wwan_force_int4 = {
> #if 1 //Added by Quectel
> .tx_fixup = qmi_wwan_tx_fixup,
> .rx_fixup = qmi_wwan_rx_fixup,
> #endif
> };
> static const struct driver_info qmi_wwan_shared = {
> #if 1 //Added by Quectel
> .tx_fixup = qmi_wwan_tx_fixup,
> .rx_fixup = qmi_wwan_rx_fixup,
> #endif
436a562,584
> #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
727d874
< {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */
790a938,939
> /*invoid to conflict EC20 module*/
> #if 0
791a941
> #endif
GobiNet Driver
[KERNEL]/drivers/usb/serial/qcserial.c
[KERNEL]/drivers/net/usb/qmi_wwan.c
root@virtual-machine:/sky9151/linux-3.10.108/drivers/usb/serial# diff qcserial.c /sky9151/linux-3.10.x/drivers/usb/serial/qcserial.c
81c81,84
< {USB_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
---
> /*invoid conflict with EC20 module*/
> #if 0
> {USB_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
> #endif
Modify Kernel Configuration
[*] Device Drivers →
-*- Network device support →
USB Network Adapters →
{*} Multi-purpose USB Networking Framework
<*> QMI WWAN driver for Qualcomm MSM based 3G and LTE modems
[*] Device Drivers →
[*] USB Support →
[*] USB Modem (CDC ACM) support
Power Management
[KERNEL]/drivers/usb/serial/option.c
[KERNEL]/drivers/usb/class/cdc-acm.c
root@virtual-machine:/linux-3.10.108/drivers/usb/class# diff cdc-acm.c /linux-3.10.x/drivers/usb/class/cdc-acm.c
546,547c546
< retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL);
< if (retval) {
---
> if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
554,555c553,554
< retval = acm_set_control(acm, acm->ctrlout);
< if (retval < 0 && (acm->ctrl_caps & USB_CDC_CAP_LINE))
---
> if (acm_set_control(acm, acm->ctrlout) < 0 &&
> (acm->ctrl_caps & USB_CDC_CAP_LINE))
557a557,558
> usb_autopm_put_interface(acm->control);
>
566,567c567
< retval = acm_submit_read_urbs(acm, GFP_KERNEL);
< if (retval)
---
> if (acm_submit_read_urbs(acm, GFP_KERNEL))
570,571d569
< usb_autopm_put_interface(acm->control);
<
588,589c586
<
< return usb_translate_errors(retval);
---
> return retval;
1003,1005d999
< /* we would crash */
< if (!data_interface || !control_interface)
< return -ENODEV;
1218a1213
> acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
1267c1262
< usb_rcvintpipe(usb_dev, epread->bEndpointAddress),
---
> acm->rx_endpoint,
1274c1269
< usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress),
---
> acm->rx_endpoint,
1366c1361,1375
<
---
> #if 1 //Added by Quectel
> //For USB Auto Suspend
> if((usb_dev->descriptor.idVendor == 0x1519) && (usb_dev->descriptor.idProduct == 0x0020))
> {
> pm_runtime_set_autosuspend_delay(&usb_dev->dev, 3000);
> usb_enable_autosuspend(usb_dev);
> }
> #endif
> #if 1 //Added by Quectel
> //For USB Remote Wakeup
> if ((usb_dev->descriptor.idVendor == 0x1519) && (usb_dev->descriptor.idProduct == 0x0020))
> {
> device_init_wakeup(&usb_dev->dev, 1); //usb remote wakeup
> }
> #endif
启动log:
启动和创建链接:
~ # quectel-CM -s cnet&
[01-01_00:00:41:440] WCDMA<E_QConnectManager_Linux&Android_V1.1.34
[01-01_00:00:41:440] quectel-CM profile[1] = cnet///0, pincode = (null)
[01-01_00:00:41:440] Find /sys/bus/usb/devices/1-2 idVendor=2c7c idProduct=0125
[01-01_00:00:41:440] Find /sys/bus/usb/devices/1-2:1.4/net/wwan0
[01-01_00:00:41:440] Find usbnet_adapter = wwan0
[01-01_00:00:41:480] Find /sys/bus/usb/devices/1-2:1.4/usbmisc/cdc-wdm0
[01-01_00:00:41:480] Find qmichannel = /dev/cdc-wdm0
[01-01_00:00:41:520] cdc_wdm_fd = 7
[01-01_00:00:41:610] Get clientWDS = 1
[01-01_00:00:41:640] Get clientDMS = 1
[01-01_00:00:41:710] Get clientNAS = 2
[01-01_00:00:41:740] Get clientUIM = 1
[01-01_00:00:41:770] Get clientWDA = 1
[01-01_00:00:41:800] requestBaseBandVersion EC20CEFILGR06A01M1G
[01-01_00:00:41:930] requestGetSIMStatus SIMStatus: SIM_READY
[01-01_00:00:41:930] requestSetProfile[1] cnet///0
[01-01_00:00:42:000] requestGetProfile[1] cnet///0
[01-01_00:00:42:030] requestRegistrationState2 MCC: 460, MNC: 1, PS: Attached, DataCap: LTE
[01-01_00:00:42:060] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED
[01-01_00:00:42:140] requestRegistrationState2 MCC: 460, MNC: 1, PS: Attached, DataCap: LTE
[01-01_00:00:42:350] requestSetupDataCall WdsConnectionIPv4Handle: 0x86aedde0
[01-01_00:00:42:410] requestQueryDataCall IPv4ConnectionStatus: CONNECTED
[01-01_00:00:42:480] ifconfig wwan0 up
[01-01_00:00:42:590] busybox udhcpc -f -n -q -t 5 -i wwan0
[01-01_00:00:42:680] udhcpc (v1.22.1) started
[01-01_00:00:42:810] Sending discover...
ping: bad address 'www.baidu.com'
[01-01_00:00:42:870] Sending select for 10.234.194.25...
[01-01_00:00:42:950] Lease of 10.234.194.25 obtained, lease time 7200
[01-01_00:00:43:060] deleting routers
route: ioctl 0x890c failed: No such process
[01-01_00:00:43:170] adding dns 120.80.80.80
[01-01_00:00:43:170] adding dns 221.5.88.88
至此,可以正常工作啦。
ping www.baidu.com测试一下
打完收工!