Are you targeting FreeBSD 13, 14, or 15? The libcuse API has changed slightly over time, so examples can differ depending on the version.
You might want to check /usr/share/examples/cuse if it’s present on your system, or look in the src tree under lib/libcuse/ and sys/dev/cuse/. Those are usually smaller and easier to digest than something like virtual_oss(8).
The blocking main loop behavior is expected — cuse_wait_and_process() is designed to sleep until an event arrives. For graceful shutdown, common approaches include installing a signal handler (e.g., SIGINT/SIGTERM) that sets a termination flag and triggers a wakeup, integrating with poll/select, or generating a dummy event to unblock the loop cleanly.
If your goal is simple IPC via a userspace character device, you may not need ioctl() initially. In many cases, read/write callbacks are sufficient depending on the semantics you need. virtual_oss(8) is large, but the core pattern is fairly small: define a cuse_methods structure, register the device, and run the event loop.
You could also grep the src tree for other libcuse users to see minimal patterns in practice.