A reusable kit for driver-development labs in embedded systems courses: a custom QEMU virtual MMIO device, a driver skeleton, and a minimal HTTP backend.
qemu/vpos_netmsg.c— aSysBusDevicethat emulatesnetmsg's register interface and translatesPOST/GETcommands into HTTP requests to the lab server.driver/netmsg.h— register definitions and the public driver API.driver/netmsg.c— a fill-in-the-blanks driver source for students.server/es.js— Express router that implements the backend protocol.server/app.js— minimal server entry point that mounts the router.
The reference driver implementation and the original lab handouts are not included. They are kept private so the kit remains usable as a graded lab; instructors who adopt the kit are expected to author their own reference solution.
+--------------+ +------------------+ +----------------------+
| VPOS shell | ---> | netmsg driver | ---> | MMIO registers |
| (post / get) | | (vh_netmsg_*) | | (CTRL/STATUS/...) |
+--------------+ +------------------+ +----------+-----------+
|
v
+------------------------------+
| QEMU device (vpos_netmsg.c) |
| - register read/write |
| - command dispatch |
| - HTTP client |
+--------------+---------------+
|
| HTTP
v
+------------------------------+
| Backend server (server/) |
| - stores per-id messages |
+------------------------------+
The diagram assumes a VPOS-like RTOS environment, but netmsg is intentionally minimal: the device exposes a plain MMIO register interface, and the driver layer can be reimplemented for any environment that can issue MMIO reads and writes.
A few choices are worth noting, since they were made specifically to expose students to ideas not commonly seen in self-contained microcontroller labs:
- MMIO register interface instead of UART or virtio. Students reason about what each register is for and design their driver around device state, rather than reusing familiar character-device patterns.
- Polling-based status interface. The driver loop checks
STATUS.BUSY,STATUS.RX_READY, andSTATUS.ERRORexplicitly. This teaches the BUSY/READY/ERROR state machine without the added complexity of interrupt handling. - HTTP backend. A command issued at the shell results in a real network round-trip to a remote server. The intent is to show that a device driver is the interface between a software abstraction and an external system, not a self-contained simulation. Students can verify their driver by posting a message from one machine and retrieving it from another.
Base address: 0x0A100000. All register accesses are 32-bit.
| Offset | Register | Direction | Bits |
|---|---|---|---|
0x00 |
NETMSG_CTRL |
R/W | bit 0: EN, bit 1: CLEAR |
0x04 |
NETMSG_STATUS |
R | bit 0: BUSY, bit 1: RX_READY, bit 2: ERROR |
0x08 |
NETMSG_ID |
R/W | bit 0–31: target message ID |
0x0C |
NETMSG_CMD |
R/W | bit 0: POST, bit 1: GET |
0x10 |
NETMSG_DATA |
R/W | one byte per access; string transfer |
NETMSG_DATA is accessed one byte at a time; the device maintains internal TX and RX buffers.
The QEMU device makes plain HTTP requests to the lab server when a command is dispatched.
POST /es/messages
Content-Type: application/json
Body: {"id": <uint>, "message": "<string>"}
Response: 200 OK with {"ok": true, "id": <uint>, "length": <uint>}
GET /es/messages/{id}
Response: 200 OK with the message in the response body (text/plain)
The reference implementation in server/es.js uses in-memory storage, so messages are lost on restart. Persistent storage can be substituted by replacing the backing Map without touching the protocol.
The kit has four pieces: the backend server, the QEMU device, the driver, and the shell commands.
cd server
npm install
node app.jsAny host reachable from QEMU works for the lab — a course VM, a small VPS, or a host on the same LAN as student machines. The server listens on port 3000 by default; override with the PORT environment variable.
Place the device source under hw/misc/ and add it to the build:
cp qemu/vpos_netmsg.c <qemu-source>/hw/misc/
echo "system_ss.add(files('vpos_netmsg.c'))" >> <qemu-source>/hw/misc/meson.buildUpdate the backend address. Open vpos_netmsg.c and edit the following macros to point at the server you deployed in step 1:
#define HTTP_HOST "your-server.example.com"
#define HTTP_PORT 3000
#define HTTP_POST_PATH "/es/messages"Register the device in the ARM virt machine. In <qemu-source>/hw/arm/virt.c, inside machvirt_init, add the following block after fdt_add_pmu_nodes(vms);:
{
DeviceState *dev = qdev_new("vpos-netmsg");
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0A100000);
}Build QEMU with the ARM target:
./configure --target-list=arm-softmmu
make -j"$(nproc)"Copy driver/netmsg.h and driver/netmsg.c into the guest source tree (in the original course, vpos/include/ and vpos/hal/io/ respectively) and add netmsg.c/netmsg.o to the build.
Students implement the TODOs in netmsg.c. The header lays out the public API (vh_netmsg_init, vh_netmsg_post, vh_netmsg_get); the skeleton comments walk through the expected register sequence.
Add post and get commands to the guest shell that call the driver. With everything in place, the commands become usable from inside the guest:
Shell>post 1234 Hello_world!
netmsg post success (id=1234, msg=Hello_world!)
Shell>get 1234
netmsg[1234] = Hello_world!
Most of the kit is independent of any particular RTOS. To use netmsg outside the original VPOS context:
- Backend runs unchanged; just deploy it somewhere reachable from QEMU.
- QEMU device runs unchanged; only the
HTTP_HOST/HTTP_PORTmacros need to be updated. - Driver needs to be ported. The header defines a minimal three-function API (
vh_netmsg_init,vh_netmsg_post,vh_netmsg_get) that maps directly onto the register interface; any environment that can do MMIO can implement it. - Shell commands can be replaced with whatever invocation surface the target course uses (a user-space CLI, a test program, etc.).
netmsg/
├── README.md
├── LICENSE
├── qemu/
│ └── vpos_netmsg.c # QEMU virtual device
├── driver/
│ ├── netmsg.h # register definitions and driver API
│ └── netmsg.c # student skeleton
└── server/
├── es.js # Express router for the backend protocol
├── app.js # minimal server entry point
└── package.json
Designed and implemented by Jeongin Yeo as part of the Embedded Systems Design course at Hanyang University. Practice sessions were co-conducted with Jinho Shin.
GPL v2.