Fading Coder

One Final Commit for the Last Sprint

Home > Tools > Content

Porting Quectel EC20 USB Modem Driver to Linux Kernel

Tools May 16 1

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.