RDK S100: Set up third-party drivers
The D-Robotics RDK S100 includes a set of drivers for a limited set of devices. This guide covers how to set up drivers that aren’t included (out-of-tree kernel modules), including building, signing, installing, and automating the process with a script. For example, our initial setup used a USB Wi-Fi adapter that is Linux compatible but not initially recognized because the system didn’t include a driver for it, so we set up an out-of-tree module.
Background
Section titled “Background”The RDK S100 comes with Ubuntu preinstalled, but it runs on a custom Linux kernel (6.1.112-rt43) that includes a limited set of prebuilt drivers, also referred to as kernel modules. Devices that use an included module are plug-and-play, while you must manually set up drivers for other devices as out-of-tree kernel modules.
The S100P’s custom Linux kernel differs from stock Ubuntu in these important ways:
| Characteristic | Implication |
|---|---|
| Custom kernel (6.1.x) | Ubuntu’s prebuilt modules (targeting 5.15) won’t work. |
| Mandated module signing | All kernel modules must be signed or they will be rejected. |
| Limited apt repository | Many standard Ubuntu packages aren’t included. |
| Kernel headers available | Drivers can be compiled on-board without a cross-compiler. |
Prerequisites
Section titled “Prerequisites”Before you begin, run the following terminal commands on the S100.
Install build dependencies:
sudo apt install -y build-essential flex bisonVerify kernel headers are installed:
ls /lib/modules/$(uname -r)/build/MakefileIf the file exists, headers are installed. If you get No such file or directory, install them:
sudo apt install linux-headers-6.1.112-rt43Prepare the kernel build environment
Section titled “Prepare the kernel build environment”Before compiling any module, run the D-Robotics prepare target to set up build scripts and tools (see Kernel Headers documentation):
sudo make -C /lib/modules/$(uname -r)/build prepareThis only needs to be done once per kernel version, or after a kernel update.
Obtain the driver source
Section titled “Obtain the driver source”Linux drivers are typically distributed as source code you must compile on the target device.
Driver source code usually comes from one of these places:
- Vendor website (e.g., Panda Wireless, PEAK-System)
- GitHub repository (e.g., openwrt/mt76)
- Kernel source tree (if the driver exists in-tree but wasn’t enabled)
Download the source code or clone it using Git.
When using upstream repositories like GitHub, make sure the code is compatible with Linux kernel 6.1. The latest main Linux branch often targets newer kernels and may fail to compile against the S100P’s 6.1 kernel.
Compile the driver
Section titled “Compile the driver”Compile instructions are typically found in a README, INSTALL, or Makefile file included with the driver source code, or on the driver’s download page or GitHub repository.
Follow the driver’s own build instructions. For most drivers, this involves running the make command to compile the source code into a loadable kernel module (.ko file).
For more information about using the make command, see Building External Modules.
The S100P ships with GCC (GNU Compiler Collection) version 11.4.0, which is the compiler used to build kernel modules. The kernel itself was compiled with version 11.3.1. You’ll see a warning about mismatching versions during builds, but it doesn’t cause issues.
If compilation fails with errors about missing functions, changed function signatures, or missing header files, the driver source likely targets a different kernel version. Look for a branch, tag, or release of the driver that targets kernel 6.1, or check if the vendor provides a compatible version.
Sign and install the driver
Section titled “Sign and install the driver”The S100P kernel only allows drivers to load if they have been cryptographically signed. This is a security measure that prevents unauthorized code from running inside the kernel. If you try to load an unsigned driver, the system displays this error:
ERROR: could not insert module: Key was rejected by serviceThe simplest way to handle signing is to use the kernel’s modules_install command, which signs the driver, copies it to the correct system directory, and registers it with the kernel in one step:
sudo make -C /lib/modules/$(uname -r)/build KERNELRELEASE=$(uname -r) M=$(pwd) modules_installIf you need to sign a driver manually (for example, when modules_install isn’t available), use the kernel’s sign-file tool:
sudo /lib/modules/$(uname -r)/build/scripts/sign-file sha512 \ /lib/modules/$(uname -r)/build/certs/module_signature.pem \ /lib/modules/$(uname -r)/build/certs/module_signature.x509 \ your_module.koLoad the driver
Section titled “Load the driver”After signing and installing the driver, load it into the running kernel using the modprobe command as follows. This step only loads the driver once and does not reload it when you reboot the OS.
sudo modprobe <your_module_name>If the driver loads successfully, there is no output. If it fails, you’ll see an error such as FATAL: Module your_module_name not found or Key was rejected by service.
Verify the driver is loaded using the lsmod command, which lists all currently loaded kernel modules:
lsmod | grep <your_module_name>If the driver is loaded, you’ll see a line with its name and memory size. No output means the driver isn’t loaded.
Configure auto-loading
Section titled “Configure auto-loading”To automatically load the driver when the OS starts, create a configuration file in the /lib/modules-load.d/ directory with your driver name:
sudo bash -c 'echo <your_module_name> > /lib/modules-load.d/<your_module_name>.conf'Reboot and verify the driver automatically loads.
Pin the kernel
Section titled “Pin the kernel”When you perform a system update on the S100, it may update the kernel and then fail to load your out-of-tree modules. Kernel updates break out-of-tree modules on the system because they overwrite the configuration files in the module directory, which produces a Module not found error.
As a result, the old modules remain loaded in memory during the update but won’t load when the OS reboots into the new kernel. If you intentionally update the kernel, you must rebuild all out-of-tree modules before rebooting.
To avoid accidental kernel updates due to a system upgrade, you can pin the kernel. Kernel pinning prevents the kernel package from updating when upgrading the system.
To pin the kernel on the S100, run this command:
sudo apt-mark hold linux-headers-6.1.112-rt43 linux-image-rdk-s100You can still safely upgrade all other packages:
sudo apt upgradeTo unpin the kernel and allow it to update:
sudo apt-mark unhold linux-headers-6.1.112-rt43 linux-image-rdk-s100After updating the kernel, rebuild all out-of-tree modules before rebooting, then pin the kernel again. You can rebuild the modules manually, but it is more efficient to do so using a script, such as the driver rebuild script.
Driver rebuild script
Section titled “Driver rebuild script”It’s faster and easier to rebuild an out-of-tree module using a script instead of rebuilding it manually.
You can configure the following s100p-module-rebuild.sh script to automate the rebuild process for an out-of-tree module on an S100. The script handles the full workflow necessary to rebuild the specified module: prepare, compile, sign, install, auto-load configuration, and loading the module.
Get the script
Section titled “Get the script”Save this script on your RDK S100 as s100p-module-rebuild.sh.
#!/bin/bash# s100p-module-rebuild.sh# General-purpose kernel module rebuild script for D-Robotics RDK S100P.# Handles: prepare, build, sign, install, depmod, and auto-load configuration.## Usage:# sudo ./s100p-module-rebuild.sh --build-dir <path> --build-cmd <command> --module-name <name>## Example:# sudo ./s100p-module-rebuild.sh \# --build-dir ~/my-driver/src \# --build-cmd "make -f Makefile.x86 MTK_COMBO_CHIP=MT7961 hif=usb" \# --module-name wlan_mt7961_usb
set -euo pipefail
# --- Defaults ---BUILD_DIR=""BUILD_CMD=""MODULE_NAME=""CLEAN_CMD=""AUTO_LOAD=trueSKIP_PREPARE=false
# --- Colors ---RED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'NC='\033[0m'
usage() { cat <<EOFUsage: sudo $0 [OPTIONS]
Required: --build-dir <path> Directory containing the driver source --build-cmd <command> Command to compile the driver (run from build-dir) --module-name <name> Name of the .ko module (without .ko extension)
Optional: --clean-cmd <command> Command to clean before building (run from build-dir) --no-auto-load Skip adding module to auto-load at boot --skip-prepare Skip kernel build environment preparation -h, --help Show this help message
Example: sudo $0 \\ --build-dir ~/Panda-Wireless/wlan_driver_1e4c921/gen4m \\ --build-cmd "make -f Makefile.x86 MTK_COMBO_CHIP=MT7961 hif=usb" \\ --module-name wlan_mt7961_usbEOF exit 1}
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# --- Parse arguments ---while [[ $# -gt 0 ]]; do case "$1" in --build-dir) BUILD_DIR="$2"; shift 2 ;; --build-cmd) BUILD_CMD="$2"; shift 2 ;; --module-name) MODULE_NAME="$2"; shift 2 ;; --clean-cmd) CLEAN_CMD="$2"; shift 2 ;; --no-auto-load) AUTO_LOAD=false; shift ;; --skip-prepare) SKIP_PREPARE=true; shift ;; -h|--help) usage ;; *) log_error "Unknown option: $1"; usage ;; esacdone
# --- Validate required arguments ---if [[ -z "$BUILD_DIR" || -z "$BUILD_CMD" || -z "$MODULE_NAME" ]]; then log_error "Missing required arguments." usagefi
if [[ $EUID -ne 0 ]]; then log_error "This script must be run with sudo." exit 1fi
if [[ ! -d "$BUILD_DIR" ]]; then log_error "Build directory does not exist: $BUILD_DIR" exit 1fi
KERN_VER=$(uname -r)KERN_BUILD="/lib/modules/${KERN_VER}/build"MODULE_FILE="${MODULE_NAME}.ko"
if [[ ! -d "$KERN_BUILD" ]]; then log_error "Kernel headers not found at ${KERN_BUILD}" log_error "Install with: sudo apt install linux-headers-6.1.112-rt43" exit 1fi
log_info "Kernel version: ${KERN_VER}"log_info "Build directory: ${BUILD_DIR}"log_info "Module name: ${MODULE_NAME}"
# --- Step 1: Prepare kernel build environment ---if [[ "$SKIP_PREPARE" = false ]]; then log_info "Preparing kernel build environment..." make -C "$KERN_BUILD" prepare > /dev/null 2>&1 log_info "Kernel build environment ready."else log_info "Skipping kernel build environment preparation."fi
# --- Step 2: Clean (optional) ---if [[ -n "$CLEAN_CMD" ]]; then log_info "Cleaning previous build..." cd "$BUILD_DIR" eval "$CLEAN_CMD" > /dev/null 2>&1 || truefi
# --- Step 3: Compile ---log_info "Compiling driver..."cd "$BUILD_DIR"# Allow build to "fail" due to firmware copy permission errorseval "$BUILD_CMD" 2>&1 | tail -5 || true
if [[ ! -f "$BUILD_DIR/$MODULE_FILE" ]]; then log_error "Build failed: ${MODULE_FILE} not found in ${BUILD_DIR}" exit 1filog_info "Compiled successfully: ${MODULE_FILE}"
# --- Step 4: Sign and install via modules_install ---log_info "Signing and installing module..."make -C "$KERN_BUILD" KERNELRELEASE="$KERN_VER" M="$BUILD_DIR" modules_installlog_info "Module signed and installed."
# --- Step 5: Configure auto-load ---if [[ "$AUTO_LOAD" = true ]]; then AUTOLOAD_CONF="/lib/modules-load.d/${MODULE_NAME}.conf" echo "$MODULE_NAME" > "$AUTOLOAD_CONF" log_info "Auto-load configured: ${AUTOLOAD_CONF}"fi
# --- Step 6: Load the module ---# Unload if already loadedif lsmod | grep -q "^${MODULE_NAME}"; then log_info "Unloading existing module..." modprobe -r "$MODULE_NAME" 2>/dev/null || rmmod "$MODULE_NAME" 2>/dev/null || truefi
log_info "Loading module..."modprobe "$MODULE_NAME"
if lsmod | grep -q "^${MODULE_NAME}"; then log_info "Module ${MODULE_NAME} loaded successfully."else log_error "Module ${MODULE_NAME} failed to load. Check dmesg for details." exit 1fi
echo ""log_info "Done! Module ${MODULE_NAME} is installed and will auto-load on boot."Run the script
Section titled “Run the script”Before running the script, make it executable on the system using the chmod command:
chmod +x s100p-module-rebuild.shTo run the script, set the required options in the following command and then execute the command. It’s best to run the script before rebooting the system after updating the kernel. However, if you accidentally reboot the system, you can still run the script to restore your out-of-tree modules.
sudo ./s100p-module-rebuild.sh \ --build-dir <path-to-driver-source> \ --build-cmd "<compile command>" \ --module-name <module-name>The script has additional options that you should avoid in most cases. Here is the complete options list.
| Option | Required | Description |
|---|---|---|
--build-dir | Yes | Directory containing the driver source |
--build-cmd | Yes | Command to compile the driver |
--module-name | Yes | Name of the .ko file (without extension) |
--clean-cmd | No | Command to clean before building |
--no-auto-load | No | Skip adding module to boot auto-load |
--skip-prepare | No | Skip kernel build environment preparation |
Here’s an example that runs the module rebuild script for the Panda PAU0F WiFi driver:
sudo ./s100p-module-rebuild.sh \ --build-dir ~/Panda-Wireless/wlan_driver_1e4c921/gen4m \ --build-cmd "make -f Makefile.x86 MTK_COMBO_CHIP=MT7961 hif=usb" \ --clean-cmd "make -f Makefile.x86 MTK_COMBO_CHIP=MT7961 hif=usb clean" \ --module-name wlan_mt7961_usbHere’s an example that runs the module rebuild script for PCAN driver based on the D-Robotics third-party driver documentation ↗:
sudo ./s100p-module-rebuild.sh \ --build-dir ~/peak-linux-driver-8.20.0/driver \ --build-cmd "make" \ --module-name pcan