If you are following this free Linux kernel development course, you already know that a watchdog driver is only half the story. The other half is how that driver describes itself to user space and to the watchdog core — what it can do, what it cannot do, and which callbacks it implements. That description lives in two structures: struct watchdog_info (the capability flags) and struct watchdog_ops (the callback table). Get these wrong and tools like wdctl, systemd, or a monitoring daemon will either crash, silently fail, or make assumptions your hardware can’t back up.
This lecture is part of our free embedded Linux course and continues directly from the previous watchdog lecture, where we built ep_soft_wdt, a software watchdog based on an hrtimer. Here we stay purely at the “what am I capable of” layer before wiring registration in the next lecture.
What You Will Learn
- Why the watchdog framework needs a separate “capabilities” struct at all
- What each WDIOF_* flag actually promises to user space
- The difference between a capability flag and a boot-status bit
- Every callback in
struct watchdog_ops, mandatory vs optional - How to write a small user-space tool that queries a live watchdog’s capabilities over ioctl
Prerequisites
- Comfortable reading kernel C and basic ioctl-based user space code
- Completed the previous lecture on the watchdog subsystem and
struct watchdog_device - A Linux VM or board with the
softdogmodule available (most distro kernels ship it)
Why Watchdog Capability Flags Exist
Watchdog hardware varies wildly. Some chips are dumb counters that can only be armed and disarmed. Others sit on a management controller that also tracks supply-voltage faults, fan failures, and SoC temperature, and can report exactly why the last reboot happened. Rather than forcing every driver to expose a fixed feature set — or forcing user space to probe each capability individually — the watchdog framework uses a single 32-bit bitmask, reported through struct watchdog_info.options, that a driver fills in once and user space reads through the WDIOC_GETSUPPORT ioctl.
This bitmask does two jobs depending on where it’s read from:
- In
watchdog_info.options: “here is what this hardware is capable of.” - In
watchdog_device.bootstatus: “here is what actually happened on the last reset,” using the same bit definitions where they overlap.
The WDIOF Flags, One By One
Below is every commonly implemented flag, explained in plain terms rather than lifted from a manual page.
| Flag | Meaning in your own words |
|---|---|
| WDIOF_OVERHEAT | Hardware can tell you the last reboot happened because a temperature sensor tripped a thermal limit. |
| WDIOF_FANFAULT | A monitored cooling fan stopped spinning and the watchdog card noticed. |
| WDIOF_EXTERN1 / WDIOF_EXTERN2 | The board has one or two dedicated external reset-request pins wired into the watchdog; a signal on either pin is a distinct reset cause. |
| WDIOF_POWERUNDER / WDIOF_POWEROVER | The watchdog can distinguish a brown-out (under-voltage) from an over-voltage event as the reset trigger. Both bits can be set together if the supply glitched across both thresholds. |
| WDIOF_CARDRESET | Only ever appears in bootstatus, never as a static capability — it means the watchdog’s own countdown expired and it reset the board itself. |
| WDIOF_PRETIMEOUT | The device can fire an early warning (interrupt or NMI) before the real timeout, giving software one last chance to log something or fail gracefully. |
| WDIOF_KEEPALIVEPING | The driver implements the WDIOC_KEEPALIVE ioctl. Without this bit set, user space pinging the watchdog will get -EOPNOTSUPP. |
| WDIOF_MAGICCLOSE | Writing the single character ‘V’ to /dev/watchdog right before closing the file descriptor tells the driver “I meant to stop you,” so the next close actually disarms the watchdog instead of leaving it running. |
Tip: The magic-close behaviour is a deliberate safety net. If a process holding /dev/watchdog open crashes or is killed without writing the ‘V’ character first, the box still reboots on timeout instead of silently losing its protection.
Note: Two fields sit alongside the flags in watchdog_info: firmware_version, a plain integer identifying the card’s firmware revision, and identity, a short human-readable string such as “ep-soft-wdt” that tools display to the user.
struct watchdog_ops — The Callback Table
Where watchdog_info advertises capabilities, watchdog_ops supplies the actual function pointers the core calls into. Two are mandatory, the rest are optional and each one you implement should have a matching capability flag set (or the core’s default handling kicks in).
| Callback | Mandatory? | What it does |
|---|---|---|
| start | Yes | Arms the watchdog hardware. |
| stop | Yes | Disarms the watchdog hardware. |
| ping | No | Sends a keep-alive. If you leave it NULL, the core falls back to calling start again on every keep-alive, which works but is wasteful on hardware with a cheaper dedicated ping register. |
| status | No | Returns the live status word answered back on WDIOC_GETSTATUS. |
| set_timeout | No | Changes the countdown length in seconds. Requires the driver to also advertise the settable-timeout flag or user space attempts will fail with -EOPNOTSUPP. |
| set_pretimeout | No | Configures the early-warning point. Pairs with WDIOF_PRETIMEOUT. |
| get_timeleft | No | Returns seconds remaining before a forced reset — useful for monitoring dashboards. |
| restart | No | Uses the watchdog hardware itself as a system restart handler, separate from its normal timeout-triggered reset path. |
| ioctl | No | Escape hatch for vendor-specific ioctls the generic core doesn’t already handle. Return -ENOIOCTLCMD for anything you don’t recognise so the core’s default handling still runs. |
Hands-On: Querying a Live Watchdog’s Capabilities
Rather than re-showing driver-side code from the previous lecture, let’s build a small, original user-space diagnostic tool, ep_wdt_info, that opens a watchdog device node and decodes exactly what we just covered. This is genuinely useful — it’s the same technique wdctl uses internally.
// ep_wdt_info.c — decode a watchdog device's capability flags
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/watchdog.h>
#include <sys/ioctl.h>
struct ep_flag_name {
unsigned int bit;
const char *label;
};
static const struct ep_flag_name ep_flags[] = {
{ WDIOF_OVERHEAT, "OVERHEAT (thermal reset detection)" },
{ WDIOF_FANFAULT, "FANFAULT (fan monitoring)" },
{ WDIOF_EXTERN1, "EXTERN1 (external reset input 1)" },
{ WDIOF_EXTERN2, "EXTERN2 (external reset input 2)" },
{ WDIOF_POWERUNDER, "POWERUNDER (brown-out detection)" },
{ WDIOF_POWEROVER, "POWEROVER (over-voltage detection)" },
{ WDIOF_CARDRESET, "CARDRESET (last reset caused by watchdog)" },
{ WDIOF_PRETIMEOUT, "PRETIMEOUT (early warning supported)" },
{ WDIOF_KEEPALIVEPING,"KEEPALIVEPING (ioctl-based ping supported)" },
{ WDIOF_MAGICCLOSE, "MAGICCLOSE ('V' disarms on close)" },
{ WDIOF_SETTIMEOUT, "SETTIMEOUT (timeout is configurable)" },
};
int main(int argc, char *argv[])
{
const char *node = (argc > 1) ? argv[1] : "/dev/watchdog";
int fd = open(node, O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
struct watchdog_info info;
if (ioctl(fd, WDIOC_GETSUPPORT, &info) < 0) {
perror("WDIOC_GETSUPPORT");
close(fd);
return 1;
}
printf("Identity : %s\n", info.identity);
printf("Firmware version: %u\n", info.firmware_version);
printf("Capabilities :\n");
for (size_t i = 0; i < sizeof(ep_flags) / sizeof(ep_flags[0]); i++) {
if (info.options & ep_flags[i].bit)
printf(" [x] %s\n", ep_flags[i].label);
}
int timeout = 0;
if (ioctl(fd, WDIOC_GETTIMEOUT, &timeout) == 0)
printf("Current timeout : %d seconds\n", timeout);
close(fd);
return 0;
}
Build and Run
$ gcc -Wall -o ep_wdt_info ep_wdt_info.c
$ sudo modprobe softdog
$ ls -l /dev/watchdog*
crw------- 1 root root 10, 130 Aug 7 10:02 /dev/watchdog
crw------- 1 root root 245, 0 Aug 7 10:02 /dev/watchdog0
$ sudo ./ep_wdt_info /dev/watchdog0
Expected output on a stock softdog-backed system:
Identity : Software Watchdog
Firmware version: 0
Capabilities :
[x] MAGICCLOSE ('V' disarms on close)
[x] KEEPALIVEPING (ioctl-based ping supported)
Current timeout : 60 seconds
Notice what’s absent: no OVERHEAT, no FANFAULT, no POWERUNDER/POWEROVER. That’s expected — softdog is a pure timer, it has no sensors behind it, so it correctly does not claim capabilities it can’t back up. Run the same tool against real server management hardware and you will typically see several more bits set.
Real-World Use Cases
- Server BMCs report POWERUNDER/POWEROVER/OVERHEAT so a monitoring stack can tell “watchdog reset” apart from “thermal shutdown” in post-mortem logs.
- Embedded gateways in cabinets often wire EXTERN1 to a physical tamper or door switch, using the watchdog’s external-reset input rather than a separate GPIO interrupt.
- Safety-critical control loops lean on PRETIMEOUT to flush state to non-volatile storage in the last milliseconds before a forced reset.
Common Mistakes and Troubleshooting
- Setting a flag without the callback: advertising WDIOF_SETTIMEOUT but leaving
set_timeoutNULL — user space believes it can change the timeout, then gets a confusing failure. - Implementing a callback without the flag: writing a
set_pretimeoutcallback but forgetting WDIOF_PRETIMEOUT — well-behaved tools will never call it because they never see the capability. - Assuming MAGICCLOSE if you didn’t add it: if your driver doesn’t explicitly implement the magic-close contract, do not set this bit — user space will trust it and may leave the box armed unexpectedly.
- Reading bootstatus once at boot only: some drivers refresh
bootstatusfrom hardware on everystatus()call rather than caching it — check your specific chip’s datasheet.
Best Practices
- Only ever set a WDIOF_* bit for a capability you have actually implemented and tested.
- Keep
identityshort, unique, and grep-friendly — it ends up in logs and monitoring dashboards. - Prefer a dedicated
pingcallback over relying on thestartfallback if your hardware has a lighter-weight keep-alive register — it reduces bus/register traffic on tight keep-alive intervals. - Return
-ENOIOCTLCMD, not a generic error, from a customioctlcallback for anything you don’t handle, so the watchdog core’s default ioctl processing still runs.
Performance and Security Considerations
The capability query path itself is cheap — it’s a single struct copy, not a hardware transaction. The real cost concerns are in what you enable: a very short pretimeout combined with a slow set_pretimeout callback can itself eat into your safety margin. On the security side, treat /dev/watchdog access as privileged (it is, by default, root-only) — a compromised unprivileged process should never be able to disarm a watchdog protecting the system.
Summary and Key Takeaways
watchdog_info.optionsadvertises hardware capability; the same bits inbootstatusreport what actually happened.- Every WDIOF_* flag you set is a promise — back it with the matching
watchdog_opscallback. startandstopare the only mandatory callbacks; everything else is opt-in.- User space can self-discover all of this cheaply through
WDIOC_GETSUPPORT, as ourep_wdt_infotool demonstrated.
Conclusion
Capability flags and the ops table are the contract between your driver and everyone else on the system — user space tools, init systems, and monitoring stacks all rely on that contract being accurate rather than aspirational. With the flags and callbacks clear, the next lecture in this free Linux kernel development course moves on to actually registering a watchdog device with the kernel, choosing a restart priority, and walking through a complete, original probe function.
FAQ
What happens if I don’t set any WDIOF flags at all?
Your driver still works for basic arm/disarm through start and stop, but user-space tools will show no advertised capabilities and will avoid calling optional ioctls like set_timeout or pretimeout queries.
Can WDIOF_POWERUNDER and WDIOF_POWEROVER both be set in bootstatus at the same time?
Yes — if the supply glitched across both thresholds during the same event, both bits can legitimately be set together.
Is WDIOF_CARDRESET ever set in watchdog_info.options?
No, it’s only meaningful as a bootstatus value describing why the last reset happened, not as a static capability.
Do I need to implement the ping callback?
No, it’s optional. If you leave it NULL, the watchdog core calls your start callback instead whenever a keep-alive is needed.
Why did softdog’s ep_wdt_info output show so few flags?
Because softdog is a pure software timer with no sensors or external inputs behind it, so it only truthfully advertises MAGICCLOSE and KEEPALIVEPING support.
What does the ioctl callback in watchdog_ops actually override?
It lets your driver handle extra, non-standard ioctl commands; if defined it takes priority over the watchdog core’s default ioctl handling unless you return -ENOIOCTLCMD.
Is set_timeout required for a working watchdog driver?
No, it’s optional — many drivers ship with a fixed compiled-in timeout and never implement set_timeout at all.
Continue Learning Linux Kernel Development, Free
Explore the full free Linux kernel development course, free Linux device drivers course, and free embedded Linux course on EmbeddedPathashala.
Browse the Course Index Next: Registering a Watchdog Device