Fading Coder

One Final Commit for the Last Sprint

Home > Tools > Content

Porting Quectel EC20 USB Modem Driver to Linux Kernel

Tools May 16 15

USB Serial Driver Modifications

Adding Vendor and Product IDs

To enable kernel recognition of the module, add its Vendor ID (VID) and Product ID (PID) definitions. Modify the drivers/usb/serial/option.c file as shown.

#define VIATELECOM_VENDOR_ID 0x15eb
#define VIATELECOM_PRODUCT_CDS7 0x0001

#define QUECTEL_VENDOR_EC20 0x2c7c
#define QUECTEL_PRODUCT_EC20 0x0125

static const struct usb_device_id option_ids[] = {
    { USB_DEVICE(QUECTEL_VENDOR_EC20, QUECTEL_PRODUCT_EC20) },
    { 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) },
    // ... additional entries
};

Implementing Zero-Packet Handling

USB bulk-out tranfsers require zero-packet termination under specific conditions. Update the drivers/usb/serial/usb_wwan.c file's usb_wwan_setup_urb function.

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 urb *urb;
    urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!urb)
        return NULL;

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

#if 1 // Zero-packet handling for Quectel modules
    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;
}

Filtering Interface Numbers

Prevent USB serial driver registration for interfaces with bInterfaceNumber >= 4 by modifying the probe function in drivers/usb/serial/option.c.

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;
    struct usb_device_descriptor *dev_desc = &serial->dev->descriptor;
    const struct option_blacklist_info *blacklist;

    if (iface_desc->bInterfaceClass == 0x08)
        return -ENODEV;

    blacklist = (void *)id->driver_info;
    if (blacklist && test_bit(iface_desc->bInterfaceNumber,
                              &blacklist->reserved))
        return -ENODEV;

    if (dev_desc->idVendor == cpu_to_le16(SAMSUNG_VENDOR_ID) &&
        dev_desc->idProduct == cpu_to_le16(SAMSUNG_PRODUCT_GT_B3730) &&
        iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA)
        return -ENODEV;

#if 1 // Exclude EC20 NDIS interfaces
    if (serial->dev->descriptor.idVendor == cpu_to_le16(QUECTEL_VENDOR_EC20) &&
        serial->interface->cur_altsetting->desc.bInterfaceNumber >= 4) {
        printk(KERN_INFO "EC20 NDIS interface filtered\n");
        return -ENODEV;
    }
#endif

    usb_set_serial_data(serial, (void *)blacklist);
    return 0;
}

Removing EC20 Support from qcserial and qmi_wwan

Comment out EC20 entries in drivers/net/usb/qmi_wwan.c and drivers/usb/serial/qcserial.c to avoid driver conflicts.

// In qmi_wwan.c
static const struct usb_device_id products[] = {
    {QMI_GOBI_DEVICE(0x05c6, 0x9225)},
    {QMI_GOBI_DEVICE(0x05c6, 0x9245)},
    {QMI_GOBI_DEVICE(0x03f0, 0x251d)},
    // {QMI_GOBI_DEVICE(0x05c6, 0x9215)}, // Disabled for EC20
    {QMI_GOBI_DEVICE(0x05c6, 0x9265)},
    {QMI_GOBI_DEVICE(0x05c6, 0x9235)},
};

// In qcserial.c
const struct usb_device_id id_table[] = {
    {USB_DEVICE(0x03f0, 0x241d)},
    {USB_DEVICE(0x03f0, 0x251d)},
    {USB_DEVICE(0x05c6, 0x9214)},
    // {USB_DEVICE(0x05c6, 0x9215)}, // Disabled for EC20
    {USB_DEVICE(0x05c6, 0x9264)},
    {USB_DEVICE(0x05c6, 0x9265)},
    {USB_DEVICE(0x05c6, 0x9234)},
};

Kernel Configuration and Testing

Enable USB serial support and relevant options via make menuconfig. After compiling and flashing the kernel, verify module detection and serial port enumeration.

$ lsusb
Bus 001 Device 001: ID 1d6b:0002
Bus 002 Device 001: ID 1d6b:0002
Bus 001 Device 002: ID 2c7c:0125
$ ls /dev/ttyUSB*
/dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3
$ microcom -s 9600 /dev/ttyUSB2
ate
OK
at+csq
+CSQ: 15,99

PPP Dial-up Configuraton

Copy quectel-chat-connect, quectel-chat-disconnect, and quectel-ppp scripts to /etc/ppp/peers/. Place pppd and chat binaries in /usr/bin/. Update the APN in quectel-chat-connect and set the serial device, username, and password in quectel-ppp.

# /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
OK AT+CGDCONT=1,"IP","cmnet",,0,0
OK ATD*99#
CONNECT

# /etc/ppp/peers/quectel-ppp
/dev/ttyUSB3 115200
user "test" password "test"

Initiate a PPP connection and verify network connectivity.

$ pppd call quectel-ppp &
...
local IP address 10.37.158.154
remote IP address 10.64.64.64
primary DNS address 211.136.17.107
secondary DNS address 211.136.20.203
$ ifconfig ppp0
ppp0      Link encap:Point-to-Point Protocol
          inet addr:10.37.158.154  P-t-P:10.64.64.64  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
$ echo "nameserver 8.8.8.8" > /etc/resolv.conf
$ ping www.baidu.com
PING baidu.com (123.125.114.144): 56 data bytes
64 bytes from 123.125.114.144: seq=0 ttl=53 time=102.081 ms

Related Articles

Efficient Usage of HTTP Client in IntelliJ IDEA

IntelliJ IDEA incorporates a versatile HTTP client tool, enabling developres to interact with RESTful services and APIs effectively with in the editor. This functionality streamlines workflows, replac...

Installing CocoaPods on macOS Catalina (10.15) Using a User-Managed Ruby

System Ruby on macOS 10.15 frequently fails to build native gems required by CocoaPods (for example, ffi), leading to errors like: ERROR: Failed to build gem native extension checking for ffi.h... no...

Resolve PhpStorm "Interpreter is not specified or invalid" on WAMP (Windows)

Symptom PhpStorm displays: "Interpreter is not specified or invalid. Press ‘Fix’ to edit your project configuration." This occurs when the IDE cannot locate a valid PHP CLI executable or when the debu...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.