Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions .github/workflows/libusb-vhid-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ name: Linux libusb Virtual HID Device Test (manual)
# Runs the device-I/O test against the HIDAPI *libusb* backend using a real
# virtual USB HID device (USB Raw Gadget on top of dummy_hcd).
#
# The hosted ubuntu-latest (azure) kernel is built without the USB gadget
# subsystem, so raw_gadget/dummy_hcd can't be loaded (or even built) there. We
# therefore run the test inside a lightweight VM (virtme-ng + QEMU) booting a
# *generic* Ubuntu kernel, whose linux-modules-extra ships dummy_hcd and
# raw_gadget. The VM shares the host filesystem, so it runs the binaries built
# on the host. The same approach works locally and on WSL2 (which also lacks
# those modules in its default kernel).
# Ubuntu 24.04 supplies a generic guest kernel and matching modules-extra
# package containing raw_gadget. Build dummy_hcd against that kernel's headers
# and run the tests in a virtme-ng/QEMU VM. The VM shares the host filesystem
# and uses the host-built binaries; the Azure host kernel need not support USB
# gadgets.
#
# Runs on demand (workflow_dispatch) or on a PR labelled 'ci-virtual-device'.

Expand All @@ -21,24 +19,28 @@ on:
jobs:
libusb-rawgadget:
if: github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'ci-virtual-device')
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 25
steps:
- uses: actions/checkout@v4
with:
path: hidapisrc

- name: Install deps + a gadget-capable generic kernel + virtme-ng
timeout-minutes: 10
run: |
set -eux
sudo apt-get update
sudo apt-get install -y \
# Use Ubuntu's primary archive for the guest kernel and VM packages.
printf '%s\n' 'https://archive.ubuntu.com/ubuntu' | sudo tee /etc/apt/apt-mirrors.txt
sudo apt update
sudo apt install -y --no-install-recommends \
build-essential cmake libudev-dev libusb-1.0-0-dev \
qemu-system-x86 virtme-ng linux-image-generic
# raw_gadget ships in the generic kernel's modules-extra; install that
# plus headers (needed to build dummy_hcd, which Ubuntu doesn't package).
KVER=$(ls -1 /lib/modules | grep -- '-generic$' | sort -V | tail -n1)
echo "generic kernel: ${KVER}"
sudo apt-get install -y "linux-modules-extra-${KVER}" "linux-headers-${KVER}"
sudo apt install -y --no-install-recommends "linux-modules-extra-${KVER}" "linux-headers-${KVER}"
find "/lib/modules/${KVER}" \( -name 'raw_gadget*' -o -name 'dummy_hcd*' \) || true

- name: Build dummy_hcd for the generic kernel (Ubuntu ships no package)
Expand All @@ -47,22 +49,20 @@ jobs:
KVER=$(ls -1 /lib/modules | grep -- '-generic$' | sort -V | tail -n1)
KMAJ=$(echo "${KVER}" | grep -oE '^[0-9]+\.[0-9]+')
mkdir -p dummyhcd
# Ubuntu packages no dummy_hcd; build it from the upstream source that
# matches the generic kernel's major version (xairy's copy tracks newer
# kernels and won't compile against an older one).
curl -fsSL -o dummyhcd/dummy_hcd.c \
# Use the upstream source matching the guest kernel's major version.
curl -fsSL --connect-timeout 15 --max-time 60 -o dummyhcd/dummy_hcd.c \
"https://raw.githubusercontent.com/torvalds/linux/v${KMAJ}/drivers/usb/gadget/udc/dummy_hcd.c"
printf 'obj-m += dummy_hcd.o\n' > dummyhcd/Makefile
make -C "/lib/modules/${KVER}/build" M="${PWD}/dummyhcd" modules
timeout --kill-after=3s 300s make -C "/lib/modules/${KVER}/build" M="${PWD}/dummyhcd" modules
sudo install -m 0644 "${PWD}/dummyhcd/dummy_hcd.ko" \
"/lib/modules/${KVER}/kernel/drivers/usb/gadget/udc/"
sudo depmod -a "${KVER}"
sudo timeout --kill-after=3s 60s depmod -a "${KVER}"

- name: Build HIDAPI + tests (libusb backend)
run: |
cmake -B build -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo \
timeout --kill-after=3s 60s cmake -B build -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DHIDAPI_WITH_LIBUSB=ON -DHIDAPI_WITH_HIDRAW=OFF -DHIDAPI_WITH_TESTS=ON
cmake --build build
timeout --kill-after=3s 300s cmake --build build

- name: Run DeviceIO_libusb inside a VM (generic kernel + raw_gadget)
run: |
Expand All @@ -84,6 +84,6 @@ jobs:
# Boot the kernel in a VM and run the guest script via --exec (vng's
# native flag; passing a complex command after '--' gets mangled). The
# script loads dummy_hcd/raw_gadget and runs the test (see it for why).
vng -v --pwd -r "${KIMG}" --exec "bash hidapisrc/.github/vmrun-libusb.sh" 2>&1 | tee vng.log
timeout --kill-after=3s 300s vng -v --pwd -r "${KIMG}" --exec "bash hidapisrc/.github/vmrun-libusb.sh" 2>&1 | tee vng.log
echo "--- VM run result ---"
grep -q "VNG_CTEST_EXIT=0" vng.log
14 changes: 7 additions & 7 deletions src/tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ for the per-push CI matrix:
is derived from Microsoft's
vhidmini2 sample and is licensed separately under the **MS-PL** (not HIDAPI's
license); see `windows/driver/README.md` and `windows/driver/LICENSE.txt`.
* **Linux / libusb** — needs the `raw_gadget` and `dummy_hcd` kernel modules,
which the hosted `ubuntu-latest` kernel is built *without* (it has no USB
gadget subsystem). The `libusb-vhid-test` workflow therefore runs the test
inside a lightweight VM (`virtme-ng` + QEMU) booting a *generic* Ubuntu kernel
whose `linux-modules-extra` ships both modules; the VM shares the host
filesystem, so it runs the host-built binaries. The same approach works
locally and on WSL2 (whose default kernel also lacks these modules).
* **Linux / libusb** — needs the `raw_gadget` and `dummy_hcd` kernel modules.
The `libusb-vhid-test` workflow uses an `ubuntu-24.04` runner and boots a
generic guest kernel in a lightweight VM (`virtme-ng` + QEMU). Its
`linux-modules-extra` package supplies `raw_gadget`; the workflow builds
`dummy_hcd` against the matching kernel headers. The VM shares the host
filesystem and runs the host-built binaries. The same approach supports
local hosts and WSL2 without USB gadget kernel support.
* **macOS** — creating an `IOHIDUserDevice` is gated by the
`com.apple.developer.hid.virtual.device` entitlement *and* an interactive
Accessibility (TCC) consent prompt, neither available on a hosted runner, so
Expand Down
Loading