Inspiration

Vehicles and robots are moving from classic CAN to CAN FD, with 64-byte frames and a faster data phase, and QNX runs many of the ECUs involved. Testing CAN FD software for QNX usually needs a CAN FD board. QEMU emulates one, the CTU CAN FD IP core on PCI (ctucan_pci); we found no public QNX driver for it. The QEMU CAN route for QNX we found, dev-can-linux, runs Linux SJA1000 drivers, which carry classic CAN only.

What it does

dev-can-ctucanfd is a QNX OS 8.0 driver built on the QNX CAN driver kit (libcan). It finds the board through the QNX PCI server, maps the controller and serves the standard QNX CAN devices, /dev/can1/rx0 and /dev/can1/tx1, with 64-byte CAN FD payloads; QNX's canctl works with it. QEMU bridges the virtual bus to Linux SocketCAN, so a QNX program exchanges CAN and CAN FD frames with candump and cansend on the host.

  • Interrupt service in bounded batches with documented calls only: when one interrupt's work budget is used up, the device's status stays pending, the level-triggered line fires again after InterruptUnmask(), and client requests are served in between.
  • Four transmit buffers with rotating priorities that keep FIFO order, bus-off and error policies, and receive framing validated before delivery.
  • A fault-injection build that replays error, bus-off and timing sequences QEMU's model does not produce.
  • ctucan-supervisor restarts the driver when it dies, and clients reopen their devices.
  • ecu-sim, a simulated battery-management ECU, sends a sequenced 64-byte status frame every 10 ms and answers a diagnostic request.
  • Two QNX nodes on one virtual CAN FD bus: two QEMU VMs, each with its own controller and driver, joined through the host's SocketCAN interface; one runs the ECU, the other a tester.

How we built it

C against the QNX SDP 8.0 CAN DDK, tested on QNX OS 8.0 x86_64 images built with mkqnximage and run under QEMU 6.2 with KVM, with a host-side test suite in Python that checks complete frames: identifier, format, length, every payload byte including padding, and sequence numbers.

Challenges we ran into

  • A lost interrupt under paced bursts: a frame arriving between the last status read and the unmask. Re-reading the status after unmask fixed it; the bounded-batch design replaced that fix.
  • Transmit order with four hardware buffers whose priorities decide what goes first: a fixed-priority scheme broke order 996 times in 1000 frames in a negative-control test; rotating priorities keep 1000 of 1000 in order.
  • QEMU 6.2 models no bit timing, error counters or bus timing, so the error paths are tested through fault injection, and every timing figure below is QEMU timing.

Accomplishments that we're proud of

  • From a clean clone, the gate test and 41 end-to-end tests pass in QEMU: every payload length 0 to 64 both ways, remote frames, 11- and 29-bit ids, four readers and four writers at once, queue overflow, 2000 + 2000 frames in both directions at once, and injected warning, error-passive, bus-off and recovery sequences.
  • Restart under load (ecu-sim at a 1 ms period, the host sending 500 frames/s into QNX): after each of three slay -s KILL dev-can-ctucanfd, telemetry resumed after gaps of 9.0, 6.5 and 8.4 ms; the kernel, the shell and the ECU process kept running and the ECU reopened its device.
  • Telemetry at a 10 ms period over 30 s: 3032 frames, CRC and sequence clean, mean period 10.000 ms, standard deviation 0.049 ms; a diagnostic request answered 1.1 ms after it was sent.
  • Two QNX nodes over 30 s: all 3,094 telemetry frames arrived with sequence and CRC clean, and all 3,000 64-byte echo round trips came back byte for byte (median round trip 3.27 ms).
  • A 44-minute codelab in QNX's codelab format, run nine times from scratch; the last run needed no change to the text.

What we learned

A QNX driver runs as an ordinary process, so it resets the hardware on every start and keeps no state from a previous instance; with a supervisor and clients that reopen, a driver crash costs one short gap in the traffic. QEMU's CTU CAN FD model ignores the acceptance-filter registers and stores every frame.

What's next

Hardware acceptance filters on CTU CAN FD silicon, a run on real CTU CAN FD hardware and on ARM (the driver compiles and links for aarch64le), and documentation suitable as a CAN DDK example.

Evidence of QNX use (for reviewers)

  • Driver: src/dev-can-ctucanfd.c (libcan callbacks transmit, devctl and event_handler, can_resmgr_* setup, pci_device_find and pci_device_attach, mmap_device_memory, InterruptUnmask, ThreadCtl(_NTO_TCTL_IO_LEVEL, ...)).
  • QEMU command line and test suite: scripts/qemu-can.sh, scripts/test-suite.sh; full output of the clean-clone run on QNX OS 8.0.0 x86_64: docs/test-results.txt.
  • Build: QNX SDP 8.0 (qcc, make); target: x86_64 QEMU image from mkqnximage; aarch64le compiles and links.

Built With

  • bash
  • c
  • can-fd
  • can-utils
  • kvm
  • libcan
  • python
  • qemu
  • qnx
  • qnx-ddk
  • qnx-sdp-8.0
  • socketcan
Share this project:

Updates

Submission history