Skip to content
Open
156 changes: 156 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
name: Build & Validate Drivers

on:
push:
branches: ['v5.4', 'v5.15', 'v6.1', 'v6.6', 'v6.12']
workflow_dispatch:

concurrency:
group: build-${{ github.ref_name }}
cancel-in-progress: true

jobs:
setup:
runs-on: ubuntu-latest
outputs:
kv: ${{ steps.m.outputs.kv }}
major: ${{ steps.m.outputs.major }}
steps:
- id: m
run: |
REF="${GITHUB_REF_NAME}"
[[ "$REF" == */merge ]] && REF="${{ github.head_ref }}"
KV="${REF#v}"
echo "kv=${KV}" >> "$GITHUB_OUTPUT"
echo "major=${KV%%.*}" >> "$GITHUB_OUTPUT"

validate-patches:
needs: setup
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Download & validate patches
shell: bash
run: |
set -euo pipefail
KV="${{ needs.setup.outputs.kv }}"
MAJ="${{ needs.setup.outputs.major }}"
curl -fSL --retry 3 -o /tmp/linux.tar.xz "https://cdn.kernel.org/pub/linux/kernel/v${MAJ}.x/linux-${KV}.tar.xz"
mkdir -p /tmp/linux-src
tar xf /tmp/linux.tar.xz -C /tmp/linux-src --strip-components=1
cd /tmp/linux-src
ERRORS=0
for pf in "$GITHUB_WORKSPACE/patches"/*.patch; do
[ -f "$pf" ] || continue
name=$(basename "$pf")
head -1 "$pf" | grep -q "No changes" && echo "⊘ $name: no changes needed" && continue
echo "→ $name"
patch --dry-run -p1 < "$pf" && echo "✓ $name" || { echo "✗ $name FAILED"; ERRORS=$((ERRORS+1)); }
done
exit $ERRORS

build-out-of-tree:
needs: setup
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: Install deps
run: sudo apt-get update -qq && sudo apt-get install -y -qq build-essential libelf-dev bc flex bison

- name: Download kernel ${{ needs.setup.outputs.kv }}
shell: bash
run: |
set -euo pipefail
KV="${{ needs.setup.outputs.kv }}"
MAJ="${{ needs.setup.outputs.major }}"
curl -fSL --retry 3 -o /tmp/linux.tar.xz "https://cdn.kernel.org/pub/linux/kernel/v${MAJ}.x/linux-${KV}.tar.xz"
tar xf /tmp/linux.tar.xz -C /tmp --strip-components=1

- name: Configure & build kernel with USB/NET subsystems
shell: bash
run: |
set -euo pipefail
cd /tmp
make allnoconfig > /tmp/allnoconfig.log 2>&1
scripts/config --enable CONFIG_64BIT || true
scripts/config --enable CONFIG_MODULES
scripts/config --enable CONFIG_PRINTK
scripts/config --enable CONFIG_PROC_FS
scripts/config --enable CONFIG_SYSFS
scripts/config --enable CONFIG_DEVTMPFS
scripts/config --enable CONFIG_TTY
scripts/config --enable CONFIG_BLOCK
scripts/config --enable CONFIG_GENERIC_ALLOCATOR
scripts/config --enable CONFIG_NET
scripts/config --enable CONFIG_INET
scripts/config --enable CONFIG_CRC32
scripts/config --enable CONFIG_CRC16
scripts/config --enable CONFIG_CRC_CCITT
scripts/config --enable CONFIG_USB
scripts/config --enable CONFIG_USB_SUPPORT || true
scripts/config --enable CONFIG_USB_COMMON || true
scripts/config --enable CONFIG_USB_ARCH_HAS_HCD || true
scripts/config --enable CONFIG_USB_ANNOUNCE_NEW_DEVICES || true
scripts/config --set-val CONFIG_USB_SERIAL y
scripts/config --enable CONFIG_USB_SERIAL_CONSOLE || true
scripts/config --enable CONFIG_USB_SERIAL_WWAN || true
scripts/config --enable CONFIG_USB_SERIAL_OPTION || true
scripts/config --enable CONFIG_USB_NET_DRIVERS || true
scripts/config --enable CONFIG_USB_ACM || true
scripts/config --enable CONFIG_WWAN || true
scripts/config --enable CONFIG_WWAN_CORE || true
scripts/config --enable CONFIG_MODULE_UNLOAD || true
scripts/config --enable CONFIG_NET_CORE || true
scripts/config --enable CONFIG_FIB_RULES || true
scripts/config --enable CONFIG_MULTIUSER || true
scripts/config --enable CONFIG_FUTEX || true
scripts/config --enable CONFIG_RATIONAL || true
scripts/config --enable CONFIG_CRC32_SLICEBY8 || true
scripts/config --disable CONFIG_STACK_VALIDATION || true
make olddefconfig > /tmp/olddefconfig.log 2>&1 || { tail -100 /tmp/olddefconfig.log; exit 1; }
grep -E 'CONFIG_(USB_SERIAL|USB_NET|WWAN|MODULES|STACK_VALIDATION)[= ]' .config | head -20 || true
make vmlinux -j$(nproc) SKIP_STACK_VALIDATION=1 HOSTCFLAGS="-Wno-error" > /tmp/vmlinux.log 2>&1 || { tail -200 /tmp/vmlinux.log; exit 1; }
make modules_prepare SKIP_STACK_VALIDATION=1 HOSTCFLAGS="-Wno-error" > /tmp/modules_prepare.log 2>&1 || { tail -200 /tmp/modules_prepare.log; exit 1; }
[ -f /tmp/vmlinux ] && echo '✓ vmlinux built'
[ -f /tmp/Module.symvers ] && echo '✓ Module.symvers present' || echo '! Module.symvers still missing; modpost warnings will be tolerated'

- name: Build out-of-tree drivers
shell: bash
run: |
set -euo pipefail
make KDIR=/tmp KBUILD_MODPOST_WARN=1 2>&1 | tee /tmp/build.log
echo "=== Build log ==="
tail -40 /tmp/build.log

- name: Verify modules
shell: bash
run: |
set -euo pipefail
ERRORS=0
for mod in option.ko usb_wwan.ko qmi_wwan.ko; do
found=$(find build/ -name "$mod" -print -quit 2>/dev/null || true)
if [ -n "$found" ]; then
echo "✓ $mod"
else
echo "✗ $mod NOT found"
ERRORS=$((ERRORS + 1))
fi
done
if grep -E 'fatal error:' /tmp/build.log; then
echo "✗ Compilation errors!"
ERRORS=$((ERRORS + 1))
fi
exit $ERRORS

summary:
needs: [setup, validate-patches, build-out-of-tree]
if: always()
runs-on: ubuntu-latest
steps:
- run: |
echo "## kernel ${{ needs.setup.outputs.kv }}"
echo "patches: ${{ needs.validate-patches.result }}"
echo "build: ${{ needs.build-out-of-tree.result }}"
[ "${{ needs.validate-patches.result }}" = "success" ] && [ "${{ needs.build-out-of-tree.result }}" = "success" ] || exit 1
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ Version 2, June 1991
This driver is licensed under the GNU General Public License v2.0 only (GPL-2.0-only),
consistent with the Linux kernel license under which the original source code is distributed.

See: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
See: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
46 changes: 46 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Quectel USB Driver Out-of-Tree Build
#
# Copies your kernel's own upstream driver sources, applies Quectel
# enhancement patches (additional device IDs), and builds the modules.
#
# Usage:
# make # uses running kernel headers
# make KDIR=/path/to/kernel # uses specified kernel source
# make install # install built modules

KDIR ?= /lib/modules/$(shell uname -r)/build
BUILD_DIR := build
KBUILD_MODPOST_WARN ?= 1

SERIAL_SRC := $(KDIR)/drivers/usb/serial
NET_SRC := $(KDIR)/drivers/net/usb

.PHONY: all clean install

all: prepare
$(MAKE) -C $(KDIR) M=$(CURDIR)/$(BUILD_DIR)/serial KBUILD_MODPOST_WARN=$(KBUILD_MODPOST_WARN) modules
$(MAKE) -C $(KDIR) M=$(CURDIR)/$(BUILD_DIR)/net KBUILD_MODPOST_WARN=$(KBUILD_MODPOST_WARN) modules

prepare:
@echo "=== Quectel USB Driver Out-of-Tree Build ==="
@mkdir -p $(BUILD_DIR)/serial $(BUILD_DIR)/net
@# Copy upstream sources from kernel tree
@test -f "$(SERIAL_SRC)/option.c" && cp "$(SERIAL_SRC)/option.c" "$(BUILD_DIR)/serial/option.c" || echo "WARN: option.c not found"
@test -f "$(SERIAL_SRC)/usb_wwan.c" && cp "$(SERIAL_SRC)/usb_wwan.c" "$(BUILD_DIR)/serial/usb_wwan.c" || echo "WARN: usb_wwan.c not found"
@test -f "$(SERIAL_SRC)/usb-wwan.h" && cp "$(SERIAL_SRC)/usb-wwan.h" "$(BUILD_DIR)/serial/usb-wwan.h" || echo "WARN: usb-wwan.h not found"
@test -f "$(NET_SRC)/qmi_wwan.c" && cp "$(NET_SRC)/qmi_wwan.c" "$(BUILD_DIR)/net/qmi_wwan.c" || echo "WARN: qmi_wwan.c not found"
@# Apply Quectel enhancement patches for build/ layout
@if [ -f patches/option.c.patch ] && ! head -1 patches/option.c.patch | grep -q "No changes"; then patch -p4 -d $(BUILD_DIR)/serial < patches/option.c.patch || echo "NOTE: option.c patch may conflict"; fi
@if [ -f patches/qmi_wwan.c.patch ] && ! head -1 patches/qmi_wwan.c.patch | grep -q "No changes"; then patch -p4 -d $(BUILD_DIR)/net < patches/qmi_wwan.c.patch || echo "NOTE: qmi_wwan.c patch may conflict"; fi
@# Write Kbuild files
@echo 'obj-m += option.o usb_wwan.o' > $(BUILD_DIR)/serial/Kbuild
@echo 'obj-m += qmi_wwan.o' > $(BUILD_DIR)/net/Kbuild
@echo "Sources prepared. Building..."

install:
$(MAKE) -C $(KDIR) M=$(CURDIR)/$(BUILD_DIR)/serial KBUILD_MODPOST_WARN=$(KBUILD_MODPOST_WARN) modules_install
$(MAKE) -C $(KDIR) M=$(CURDIR)/$(BUILD_DIR)/net KBUILD_MODPOST_WARN=$(KBUILD_MODPOST_WARN) modules_install
@echo "Done. Run: depmod -a && modprobe -r option qmi_wwan && modprobe option && modprobe qmi_wwan"

clean:
rm -rf $(BUILD_DIR)
80 changes: 30 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,45 @@
# Quectel Linux USB Driver
# Quectel USB Driver Enhancements — Linux 6.12

Out-of-tree and in-kernel-patch drivers for Quectel cellular modems, organized by Linux kernel LTS version.
Out-of-tree build support and in-kernel patches for comprehensive Quectel
modem support on Linux kernel **6.12**.

## Quick Start

```bash
# 1. Find your kernel version
uname -r # e.g. 5.15.0-100-generic

# 2. Checkout the matching branch
git clone https://github.com/bacnh85/Quectel_Linux_USB_Driver.git
cd Quectel_Linux_USB_Driver
git checkout v5.15 # use your major.minor version

# 3. Follow the README.md on that branch
```

## Branches
## Supported Devices

| Branch | Kernel | Quectel Devices | Patch Size |
|--------|--------|----------------|------------|
| `v5.4` | 5.4.x LTS | +30 products | 59 lines |
| `v5.15` | 5.15.x LTS | +25 products | 54 lines |
| `v6.1` | 6.1.x LTS | +21 products | 44 lines |
| `v6.6` | 6.6.x LTS | +7 products | 16 lines |
| `v6.12` | 6.12.x | Full mainline | No patch needed |
EC200(A/S/T/U), EC25, EC21, EG91, EG95, EG912Y, EG916Q, EM05 (all variants),
EM06, EM12, EM060K, EM061K, RM500Q, RM500K, RM520N, RM500U, RG650V, BG95, BG96

## What's in Each Branch

All driver sources are **upstream Linux kernel code** enhanced with Quectel device IDs backported from kernel 6.12.

```
drivers/usb/serial/option.c ← enhanced with all Quectel USB serial IDs
drivers/usb/serial/usb_wwan.c ← upstream (no Quectel-specific changes)
drivers/usb/serial/usb-wwan.h ← upstream header
drivers/net/usb/qmi_wwan.c ← enhanced with all Quectel QMI IDs
patches/option.c.patch ← diff: vanilla → enhanced (for in-kernel)
patches/qmi_wwan.c.patch ← diff: vanilla → enhanced (for in-kernel)
Makefile ← out-of-tree module build
```
## Quick Start

## Two Build Methods
### Out-of-Tree Build (Recommended)

### Method 1: Out-of-Tree (Recommended)
```bash
make && sudo make install
sudo modprobe option qmi_wwan
make KDIR=/lib/modules/$(uname -r)/build
sudo make install
sudo depmod -a
```

### Method 2: In-Kernel Patch
### In-Kernel Patch

```bash
cd /path/to/kernel-source
patch -p1 < /path/to/Quectel_Linux_USB_Driver/patches/option.c.patch
cd /path/to/linux-6.12
patch -p1 < patches/option.c.patch
patch -p1 < patches/qmi_wwan.c.patch
```

## Supported Quectel Modules
## Files

**2G/3G:** UC15, UC20
**4G LTE:** EC20, EC21, EC25, EG91, EG95, BG96, BG95, AG35, EP06, EM12, EC200S, EC200T, EC200A, EC200U, EG912Y, EG916Q
**5G NR:** RM500Q, RM520N, RM500K, RM500U, RG650V, EM05G series, EM060K/EM061K series, EM160R-GL
| File | Description |
|------|-------------|
| `Makefile` | Out-of-tree build: copies from kernel, patches, builds |
| `patches/option.c.patch` | USB serial device IDs for Quectel modems |
| `patches/qmi_wwan.c.patch` | QMI network device IDs for Quectel modems |

## License
## Branches

GPL-2.0-only — same as the Linux kernel
| Branch | Kernel |
|--------|--------|
| `v5.4` | 5.4 LTS |
| `v5.15` | 5.15 LTS |
| `v6.1` | 6.1 LTS |
| `v6.6` | 6.6 LTS |
| `v6.12` | 6.12 |
1 change: 1 addition & 0 deletions patches/option.c.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# No changes needed
1 change: 1 addition & 0 deletions patches/qmi_wwan.c.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# No changes needed
Loading