Use this guide when you need a Raspberry Pi USB HID gadget that can wake a sleeping or suspended host by sending keyboard input.
Warning
This is an advanced workflow. It installs and runs a custom Raspberry Pi kernel and is not part of the stock Bluetooth-2-USB install path.
- this installs a custom Raspberry Pi kernel
- rollback should be prepared before the first reboot
- host suspend and remote wake behavior remain platform-specific even with the patch
- the tested setups below are validated for this project
- the additional build paths below are provided for unvalidated targets that follow the same custom-kernel and read-only flow
- Raspberry Pi 4 Model B Rev 1.4:
patched
rpi-6.12.ykernel6.12.81-b2u-wake,/boot/config-$(uname -r)installed, keyboard-onlywakeup_on_write, and end-to-end wake from host suspend confirmed through normal Bluetooth-2-USB keyboard input - Raspberry Pi Zero W:
patched
rpi-6.12.ykernel6.12.81-b2u-wake, validated ARM32 build and boot path, keyboard-onlywakeup_on_write, and end-to-end wake from host suspend confirmed through normal Bluetooth-2-USB keyboard input
The wake path needs both of the following:
- a patched Raspberry Pi kernel that adds remote wakeup support to the
dwc2gadget driver and awakeup_on_writeconfigfs attribute to the HID gadget function - a Bluetooth-2-USB runtime that enables
wakeup_on_write=1for the keyboard HID function when that attribute exists
Important
Without the kernel patch, the runtime cannot wake suspended hosts.
- Raspberry Pi Linux issue
#3977: raspberrypi/linux#3977 - PiKVM reference patch: https://github.com/pikvm/packages/blob/6d1fe298d7ad13a82cf9c6d3645866a443cde8f0/packages/linux-rpi-pikvm/1001-pikvm-hid-remote-wakeup-support.patch
General build dependencies:
sudo apt install bc bison flex libssl-dev make libc6-dev libncurses5-dev64-bit cross toolchain for Pi 4B, Pi 5, and Zero 2 W:
sudo apt install crossbuild-essential-arm6432-bit cross toolchain for Pi Zero W, Pi 4B 32-bit, and Zero 2 W 32-bit:
sudo apt install crossbuild-essential-armhfIf the ARM32 GCC toolchain is unavailable, the Zero W path can also be built with a complete LLVM toolchain. That fallback is validated for this project but is still a fallback, not the preferred default.
mkdir -p ~/src
cd ~/src
git clone --depth=1 --branch rpi-6.12.y https://github.com/raspberrypi/linux.git rpi-linux-wakeup
cd rpi-linux-wakeup
git switch -c b2u/rpi-6.12.y-remote-wakeupApply the patch:
curl -L \
https://raw.githubusercontent.com/pikvm/packages/6d1fe298d7ad13a82cf9c6d3645866a443cde8f0/packages/linux-rpi-pikvm/1001-pikvm-hid-remote-wakeup-support.patch \
-o 1001-pikvm-hid-remote-wakeup-support.patch
patch -p1 < 1001-pikvm-hid-remote-wakeup-support.patchUse a fixed local version suffix:
-b2u-wake
Use this matrix as the source of truth for board-specific build targets, custom
kernel image names, and the boot initramfs name Raspberry Pi firmware expects
when auto_initramfs=1 is enabled.
| Target | Status | Build target | Expected image | Boot initramfs target | Notes |
|---|---|---|---|---|---|
| Raspberry Pi 4B | validated | ARCH=arm64, bcm2711_defconfig, KERNEL=kernel8, CROSS_COMPILE=aarch64-linux-gnu- |
kernel8-b2u-wake.img |
initramfs8-b2u-wake |
Tested on Raspberry Pi 4 Model B Rev 1.4 |
| Raspberry Pi Zero W | validated | ARCH=arm, bcmrpi_defconfig, KERNEL=kernel, CROSS_COMPILE=arm-linux-gnueabihf- |
kernel-b2u-wake.img |
initramfs-b2u-wake |
GCC path validated; LLVM fallback also validated |
| Raspberry Pi 5 | unvalidated | ARCH=arm64, bcm2712_defconfig, KERNEL=kernel_2712, CROSS_COMPILE=aarch64-linux-gnu- |
kernel_2712-b2u-wake.img |
initramfs_2712-b2u-wake |
USB-C gadget path shares the board's USB-C connectivity |
| Raspberry Pi 4B 32-bit | unvalidated | ARCH=arm, bcm2711_defconfig, KERNEL=kernel7l, CROSS_COMPILE=arm-linux-gnueabihf- |
kernel7l-b2u-wake.img |
initramfs7l-b2u-wake |
Use only for 32-bit Pi OS on Pi 4 |
| Raspberry Pi Zero 2 W 64-bit | unvalidated | ARCH=arm64, bcm2711_defconfig, KERNEL=kernel8, CROSS_COMPILE=aarch64-linux-gnu- |
kernel8-b2u-wake.img |
initramfs8-b2u-wake |
Shares the Pi 4B 64-bit image/initramfs naming |
| Raspberry Pi Zero 2 W 32-bit | unvalidated | ARCH=arm, bcm2709_defconfig, KERNEL=kernel7, CROSS_COMPILE=arm-linux-gnueabihf- |
kernel7-b2u-wake.img |
initramfs7-b2u-wake |
32-bit only |
cd ~/src/rpi-linux-wakeup
export JOBS="$(nproc)"
export KERNEL=kernel8
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig
scripts/config --set-str LOCALVERSION "-b2u-wake"
make -j"${JOBS}" ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- LOCALVERSION= Image modules dtbs
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- LOCALVERSION= kernelreleasePass LOCALVERSION= on the build and kernelrelease commands so the final
release string stays ...-b2u-wake instead of ...-b2u-wake+.
Preferred GCC path:
cd ~/src/rpi-linux-wakeup
export JOBS="$(nproc)"
export KERNEL=kernel
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcmrpi_defconfig
scripts/config --set-str LOCALVERSION "-b2u-wake"
make -j"${JOBS}" ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOCALVERSION= zImage modules dtbs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOCALVERSION= kernelreleaseValidated LLVM fallback:
cd ~/src/rpi-linux-wakeup
export JOBS="$(nproc)"
export KERNEL=kernel
make O=out/pi0w LLVM=1 ARCH=arm bcmrpi_defconfig
scripts/config --file out/pi0w/.config --set-str LOCALVERSION "-b2u-wake"
scripts/config --file out/pi0w/.config --disable BCM2835_FAST_MEMCPY
make O=out/pi0w LLVM=1 ARCH=arm syncconfig
make -j"${JOBS}" O=out/pi0w LLVM=1 ARCH=arm LOCALVERSION= zImage modules dtbs
make O=out/pi0w LLVM=1 ARCH=arm LOCALVERSION= kernelrelease
cp out/pi0w/.config "out/pi0w/config-$(make O=out/pi0w LLVM=1 ARCH=arm LOCALVERSION= kernelrelease)"Notes for the LLVM fallback:
- disabling
CONFIG_BCM2835_FAST_MEMCPYavoids the tested ARM32 LLVM build failure on Raspberry Pi specific assembly - keep
-b2u-wakein.config - pass
LOCALVERSION=on the build andkernelreleasecommands so the final release string stays...-b2u-wakeinstead of...-b2u-wake+
Warning
These build paths are unvalidated for this project. They are included so you
can build and deploy the matching custom kernel and let
bluetooth_2_usb readonly enable install the corresponding boot initramfs
automatically when you later enable read-only mode. Use the target
matrix above for the expected image and initramfs filenames.
cd ~/src/rpi-linux-wakeup
export JOBS="$(nproc)"
export KERNEL=kernel_2712
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2712_defconfig
scripts/config --set-str LOCALVERSION "-b2u-wake"
make -j"${JOBS}" ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- LOCALVERSION= Image modules dtbs
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- LOCALVERSION= kernelreleasecd ~/src/rpi-linux-wakeup
export JOBS="$(nproc)"
export KERNEL=kernel7l
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2711_defconfig
scripts/config --set-str LOCALVERSION "-b2u-wake"
make -j"${JOBS}" ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOCALVERSION= zImage modules dtbs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOCALVERSION= kernelreleasecd ~/src/rpi-linux-wakeup
export JOBS="$(nproc)"
export KERNEL=kernel8
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig
scripts/config --set-str LOCALVERSION "-b2u-wake"
make -j"${JOBS}" ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- LOCALVERSION= Image modules dtbs
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- LOCALVERSION= kernelreleasecd ~/src/rpi-linux-wakeup
export JOBS="$(nproc)"
export KERNEL=kernel7
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
scripts/config --set-str LOCALVERSION "-b2u-wake"
make -j"${JOBS}" ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOCALVERSION= zImage modules dtbs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOCALVERSION= kernelreleaseCopy the built kernel image, modules, DTBs, overlays, and matching
config-<kernelrelease> to the Pi. Then set the custom kernel image in
config.txt using the image name from the target matrix above.
With auto_initramfs=1, Raspberry Pi firmware derives the boot initramfs name
from the kernel image name. Use the matching boot initramfs target from the
same matrix when checking or troubleshooting boot artifacts.
Important
Do not treat config-<kernelrelease> or the matching module tree as optional
when you plan to enable read-only mode with a custom kernel.
When you later enable read-only mode,
bluetooth_2_usb readonly enable ensures a bootable initramfs exists for the
running kernel and installs or reuses the matching boot initramfs file
automatically. That path depends on the running kernel being fully installed on
the Pi:
- install
/lib/modules/<kernelrelease> - install
/boot/config-<kernelrelease>or otherwise keep/proc/config.gzavailable - keep the custom kernel image selected in
config.txtconsistent with the running release
If those artifacts are missing, bluetooth_2_usb readonly enable aborts
instead of trying to guess a bootable initramfs layout from documentation alone.
Important
Keep the stock kernel entry available so rollback is trivial.
After reboot, confirm:
- the custom kernel is running
uname -r- the wakeup attribute exists
sudo -n find /sys/kernel/config/usb_gadget -path '*/functions/hid.*/*wakeup_on_write' -print- the gadget still advertises remote wakeup and the keyboard function has
wakeup_on_write=1
sudo -n grep -H . /sys/kernel/config/usb_gadget/*/functions/hid.*/wakeup_on_write- the normal Bluetooth-2-USB checks still pass
sudo bluetooth_2_usb smoketest --verbose
sudo bluetooth_2_usb debug --duration 10- a real host suspend and wake test succeeds through normal keyboard input
If the first post-reboot smoketest fails on Bluetooth power, also inspect
persisted systemd-rfkill Bluetooth soft-block state under
/var/lib/systemd/rfkill.
Rollback should be simple:
- keep the stock kernel image and config in place
- restore
config.txtto the stock kernel entry - reboot
Warning
If the custom kernel fails to boot cleanly, use console or the boot medium from another machine to restore the stock kernel selection.
- this is still a custom-kernel workflow, not a stock-project feature
- host suspend and wake behavior depend on the host platform as well as the Pi
- Pi 5, Pi 4B 32-bit, and Zero 2 W 32/64-bit are unvalidated build paths